diff --git a/.github/workflows/build-sndctrl-dll.yml b/.github/workflows/build-sndctrl-dll.yml new file mode 100644 index 00000000..5f6259f0 --- /dev/null +++ b/.github/workflows/build-sndctrl-dll.yml @@ -0,0 +1,43 @@ +name: Build SndCtrl.dll (Windows) + +# Manual, on-demand build of the Windows native audio library SndCtrl.dll with MSVC. +# It ONLY builds the DLL and holds it as a workflow artifact — it does not attach it to a +# release or commit it. Download the artifact and commit src/main/resources/SndCtrl.dll by hand +# after changing the C++ sources under src/main/cpp/ (e.g. when SndCtrlNative's package changes). +on: + workflow_dispatch: + +jobs: + build: + runs-on: windows-2022 + steps: + - uses: actions/checkout@v4 + + # The build needs the Windows JNI headers (include/ + include/win32/jni_md.h), nothing more. + - name: Set up JDK (for JNI headers) + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '21' + + - name: Configure (CMake + MSVC) + shell: pwsh + run: cmake -S src/main/cpp -B build -G "Visual Studio 17 2022" -A x64 "-DWIN_JDK_HOME=$env:JAVA_HOME" + + - name: Build (Release) + shell: pwsh + run: cmake --build build --config Release + + # Log the exported JNI entry points so a package mismatch is visible in the run output. + # Best-effort: dumpbin is only on PATH inside a VS dev shell, so never fail the build on it. + - name: Show exported JNI symbols + shell: pwsh + continue-on-error: true + run: dumpbin /exports build\Release\SndCtrl.dll | Select-String "Java_|JNI_" + + - name: Upload SndCtrl.dll + uses: actions/upload-artifact@v4 + with: + name: SndCtrl-dll + path: build/Release/SndCtrl.dll + if-no-files-found: error diff --git a/CLAUDE.md b/CLAUDE.md index ccbec0e5..44f0a703 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -84,16 +84,20 @@ injection, and cross-cutting communication uses the **CDI event bus** (`jakarta. fire + `@Observes`) heavily rather than direct calls. `docs/events.md` catalogs the events with their firers and observers — keep it current when you add or remove an event. -**Hardware path (`hid/`, `device/`):** `DeviceScanner` discovers HID devices via hid4java; -`DeviceCommunicationHandler` (one per device, own thread + queue) reads knob/button input and writes -RGB/output. `Device` subclasses (`PCPanelMini/Pro/RGB`) model each hardware variant. Physical input +**Hardware path (`device/`):** the device layer is the hardware-abstraction layer (HAL) and is **not** +an integration — it provides no commands. `DeviceScanner` (in `device/provider/pcpanel/`) discovers HID +devices via hid4java; `DeviceCommunicationHandler` (one per device, own thread + queue, same package) +reads knob/button input and writes RGB/output. `Device` subclasses (`PCPanelMini/Pro/RGB`, in `device/`) +model each hardware variant; `DeviceHolder` (in `device/`) is the cross-provider registry. Physical input becomes a `PCPanelControlEvent` / `ButtonClickEvent` on the event bus. **Device providers (`device/provider/`, `device/descriptor/`):** the device layer is generalized so PCPanel is one `DeviceProvider` among several — providers are `@ApplicationScoped` beans discovered via `Instance` (NOT build-time stereotypes; every build contains all of them). -`DeviceScanner` is the `"pcpanel"` HID provider; `DeejSerialProvider` (serial, jSerialComm) and -`MidiProvider` (`javax.sound.midi`) are external providers. A device is described by a data +`DeviceScanner` is the `"pcpanel"` HID provider (`device/provider/pcpanel/`); `DeejSerialProvider` +(serial, jSerialComm, `device/provider/deej/`) and +`MidiProvider` (`javax.sound.midi`, `device/provider/midi/`) are external providers; each provider +absorbs its own IO transport (e.g. `SerialTransport`/`JSerialComm*` under `deej/`). A device is described by a data `DeviceDescriptor` (analog/digital inputs with source ranges, light/analog outputs, capabilities) rather than the `DeviceType` enum, which is now PCPanel-provider-internal. Each provider normalizes its raw analog values to the canonical **0–255** internal domain at its edge (PCPanel RGB 0–100, Deej @@ -105,15 +109,21 @@ paths (lighting, `OutputInterpreter.sendInit`) against `deviceType() == null`. ` `PcDeviceComponent` for PCPanel, else `GenericDeviceComponent`). Full design + per-phase status: `docs/device-layer-generalization-plan.md`. -**Command model (`commands/`):** A user's per-dial/button configuration is a `Commands` (list of -`Command` subclasses in `commands/command/` — e.g. `CommandVolumeProcess`, `CommandKeystroke`, -`CommandObs`, `CommandMedia`). `CommandDispatcher` maps incoming control events to the configured -commands and executes them. Commands are JSON-polymorphic and are part of the generated TS contract. - -**Native audio abstraction (`cpp/`):** `ISndCtrl` is the OS-audio facade (volume/mute/default device, -focus app). Implementations are selected at **build time** by platform stereotypes: -`@WindowsBuild` (`SndCtrlWindows` → JNI to `SndCtrl.dll` via `SndCtrlNative`, source in -`src/main/cpp/`) and `@LinuxBuild` (`SndCtrlPulseAudio` via JNA/PulseAudio). These stereotypes wrap +**Command model (`commands/` = engine; `integration/*/command/` = the commands):** A user's +per-dial/button configuration is a `Commands` (list of `Command` subclasses). `commands/` holds only the +engine — `Command`, the `Dial/Button/DeviceAction` SPIs, `CommandDispatcher`, `DialValue`, the +`@CommandMeta`/`CommandModule` registry. Each concrete command lives in its feature's package, e.g. +`integration.volume.command.CommandVolumeProcess`, `integration.keyboard.command.CommandKeystroke`/ +`CommandMedia`, `integration.obs.command.CommandObs`. Commands are JSON-polymorphic (`@JsonTypeName` +ids, decentralized registry — see `docs/feature-module-structure.md`) and part of the generated TS +contract. **A package is an `integration` only if it provides commands** — providers/HAL/infra are not. + +**Native audio abstraction (`integration/volume/platform/`):** `ISndCtrl` is the OS-audio facade +(volume/mute/default device, focus app) — it is the backend the volume commands drive, so it lives in +the volume feature. Implementations are selected at **build time** by platform stereotypes: +`@WindowsBuild` (`SndCtrlWindows` → JNI to `SndCtrl.dll` via `SndCtrlNative`, both in +`integration/volume/platform/windows/`; C++ source in `src/main/cpp/`) and `@LinuxBuild` +(`SndCtrlPulseAudio` in `platform/linux/`, via JNA/PulseAudio). These stereotypes wrap Quarkus `@IfBuildProperty(name="pcpanel.build.os", ...)` keyed off `pcpanel.build.os` (set at build time from `os.detected.name`), so **a given build only contains one platform's beans** — guard optional platform beans with `Instance` injection, and use `CdiHelper` to fetch beans from @@ -129,11 +139,14 @@ and immutable distros persist settings without a host grant. `PcPanelRoot.resolv source of truth: `Main` publishes it as the `pcpanel.root` system property for the native image, and the non-CDI `FileChecker`/`HidDebug` call it directly. See `linux.md` for the user-facing details. -**Frontend bridge (`rest/`):** JAX-RS resources under `/api` (`DeviceResource`, `CommandsResource`, -`SettingsResource`, …) plus a single websocket `EventWebSocket` at `/ws/events`. The backend pushes -device/state snapshots (DTOs in `rest/model/`) to the Angular UI over the socket; `EventBroadcaster` -fans CDI events out to connected clients. There is no separate window framework — the "UI" is the -browser served by Quinoa. +**Frontend bridge (`rest/`):** the **shared** JAX-RS + websocket bridge: `SettingsResource`, +`PlatformResource`, `SystemResource`, `IconResource`/`ProcessResource` (the app/process picker, shared +across features), `EventWebSocket` at `/ws/events`, `EventBroadcaster`, `LocalHttpGuard`, and the +`rest/model/` DTO+WS contract. Feature-specific resources live with their feature instead: +device-management REST in `device/rest/` (`DeviceResource`, `SerialResource`, `MidiResource`), +volume/overlay REST in `integration/volume/`, each external connector's REST in `integration//rest/`. +The backend pushes device/state snapshots to the Angular UI over the socket. There is no separate window +framework — the "UI" is the browser served by Quinoa. **Web-exposure security model:** the API is unauthenticated, so it must stay reachable only from the local machine. Two layers enforce this: `quarkus.http.host=127.0.0.1` keeps other hosts off, and @@ -146,9 +159,13 @@ the backstop. Toggle with `pcpanel.http.local-only` (default true). This does ** *local* callers — defending against other processes on the same machine would need a token and is out of scope. -**Integrations:** `obs/` (OBS websocket), `voicemeeter/` (JNA), `wavelink/` + `dev/niels/wavelink/` -(Elgato Wave Link RPC client), `osc/`, `mqtt/` (Eclipse Paho mqttv5), `homeassistant/`. `overlay/` -draws an on-screen volume overlay: a Win32 JNA layered window on Windows (`Win32VolumeOverlay`) and a +**Integrations (`integration/*` — command-providing features only):** the external connectors +`integration/obs/` (OBS websocket), `voicemeeter/` (JNA), `wavelink/` + `dev/niels/wavelink/` (Elgato +Wave Link RPC client), `osc/`, `mqtt/` (Eclipse Paho mqttv5), `homeassistant/`, `discord/`; plus the +feature families `volume/`, `keyboard/`, `program/`, `analogbands/`, `profile/`, and `device/` (the +brightness command only). Each owns its `command/` + `CommandModule` and (where applicable) its REST, +SPI impls, and service. The on-screen volume overlay lives in `integration/volume/overlay/`: a Win32 JNA +layered window on Windows (`Win32VolumeOverlay`) and a desktop-drawn OSD over D-Bus on Linux/Wayland (`LinuxOverlay`, AWT-free) — KDE Plasma's native volume OSD (`org.kde.osdService.volumeChanged`, the same real-time bar as Plasma's own volume keys) when plasmashell is on the bus, **else no overlay** (clean no-op). A notification fallback was deliberately @@ -158,11 +175,11 @@ so those `Save` settings don't apply on Linux (the settings UI greys them out). Selection is the runtime `Platform` check in `Overlay.createOverlay()`. `util/tray/` is the system tray (Wayland uses the D-Bus StatusNotifierItem protocol via dbus-java). -`homeassistant/` is *outbound* control (the app drives Home Assistant), distinct from the MQTT -auto-discovery in `mqtt/` (which lets Home Assistant discover the app). It holds both its own command -types (`homeassistant/command/`, kept off the big `commands/command/` pile per the action-package -convention — Jackson `Id.CLASS` makes location irrelevant, but the typescript-generator needs the -`com.getpcpanel.homeassistant.command.**` classPattern in `pom.xml`) and a minimal REST client +`integration/homeassistant/` is *outbound* control (the app drives Home Assistant), distinct from the +MQTT auto-discovery in `integration/mqtt/` (which lets Home Assistant discover the app). It holds both +its own command types (`integration/homeassistant/command/` — every feature's commands live in its own +`command/` package, all picked up by the single `com.getpcpanel.**.command.**` typescript-generator +classPattern in `pom.xml`) and a minimal REST client (`HomeAssistantClient`, JDK `HttpClient`, no extra dependency). Multiple servers are configured in settings (`Save.homeAssistantServers`); a command with a blank server id auto-resolves to the only configured server (the UI also auto-selects it). **Actions are authored as pasted HA "action" YAML** @@ -197,7 +214,8 @@ Key constraints baked into those args, change with care: `quarkus-awt` is dropped via the `os-non-mac` profile, and the `os-mac` profile defers the whole AWT/Java2D/Swing/ImageIO subsystem to run-time). So macOS must never *call* AWT: the overlay is a no-op (`NoOpOverlayWindow`), icons are disabled, keystrokes use CoreGraphics `CGEvent` - (`com.getpcpanel.cpp.osx.OsxKeyboard`), the tray and `java.awt.Desktop` are skipped. JNA classes + (`com.getpcpanel.integration.keyboard.platform.osx.OsxKeyboard`, the macOS `Keyboard` impl), the tray + and `java.awt.Desktop` are skipped. JNA classes that run `Native.load` in their initializer must be `--initialize-at-run-time` (narrowly, by class — a package-wide directive would wrongly catch Quarkus's build-time CDI `_Bean` objects). - Windows-only GUI-subsystem linker flags (`/SUBSYSTEM:WINDOWS`, `/ENTRY:mainCRTStartup`) are MSVC @@ -298,9 +316,9 @@ Full reference: [`docs/mcp-server.md`](docs/mcp-server.md). ## Conventions - Lombok is used throughout (`@Data`, `@Log4j2`, `@RequiredArgsConstructor`, etc.). `@Log4j2` is the - logging annotation (backed by jboss-logmanager). Note the `cpp` package overrides this with - **fluent** accessors (`src/main/java/com/getpcpanel/cpp/lombok.config`): `AudioDevice`/`AudioSession` - use `name()`/`volume()`/`muted()`, not `getName()`. + logging annotation (backed by jboss-logmanager). Note the audio-facade package overrides this with + **fluent** accessors (`src/main/java/com/getpcpanel/integration/volume/platform/lombok.config`): + `AudioDevice`/`AudioSession` use `name()`/`volume()`/`muted()`, not `getName()`. - Nullability annotated with `javax.annotation.@Nullable/@Nonnull` (JSR-305) — these feed the TS generator's optional-property detection. - `.editorconfig` defines formatting and a large set of IntelliJ inspection settings; follow it. diff --git a/docs/events.md b/docs/events.md index d7fa9afe..c6c73371 100644 --- a/docs/events.md +++ b/docs/events.md @@ -66,6 +66,7 @@ up to date when you add or remove an event or an observer. | `VoiceMeeterDirtyEvent` | `Voicemeeter` | `VoiceMeeterMuteService` | | `VoiceMeeterMuteEvent` | `VoiceMeeterMuteService` | `VoiceMeeterMuteResolver` (mute-colour) | | `WaveLinkChangedEvent` | `WaveLinkService` (Wave Link state incl. mute changed) | `MuteColorService` | +| `DiscordChangedEvent` | `DiscordService` (Discord voice/mute/deafen state changed) | `MuteColorService` | | `MuteOverridesDirtyEvent` | mute-colour resolvers (e.g. `VoiceMeeterMuteResolver` after caching a mute change) | `MuteColorService` | | `MqttStatusEvent` | `MqttService` | `MqttDeviceService` | diff --git a/docs/feature-module-structure.md b/docs/feature-module-structure.md new file mode 100644 index 00000000..ad90af67 --- /dev/null +++ b/docs/feature-module-structure.md @@ -0,0 +1,147 @@ +# Feature-module structure (plugin-style refactor) + +Status: **complete.** The app is reorganized so that each user-facing feature owns all of its code in +one package, implementation details are hidden behind small seams, and adding a feature means creating +a package and implementing a few SPIs rather than editing shared registries. The full JVM suite passes +(437 tests), `tsc` is clean, the native-image coverage/parity guards are green, and a native build +succeeds. This document is the source of truth for the end state. + +We are **not** supporting dynamically-loaded third-party plugins (no separate classloaders / jars). +"Plugin-style" here means *internal modularity*: features are discovered through CDI + a handful of +annotations rather than hand-wired into central lists. + +## The organising principle + +> **A package under `com.getpcpanel.integration.*` is a feature that provides commands.** +> If a piece of code provides no `Command`, it is not an integration — it is the engine, the hardware +> abstraction layer, a platform backend, or shared infrastructure, and it lives accordingly. + +This single rule decides where everything goes, and resolves the cases that were previously muddled: + +- A **device provider** (the PCPanel HID scanner, Deej, MIDI) supplies hardware *input*, not commands — + so it belongs in the device HAL (`com.getpcpanel.device.provider.*`), **never** under `integration/`. +- **Sleep/wake detection**, the **device-management REST**, and the **device colour services** provide + no commands — they are device-subsystem infrastructure, not integrations. +- The **OS audio facade** and the **keyboard/media backends** are what the volume/keyboard *commands* + drive, so they live inside those features (`integration/volume`, `integration/keyboard`). + +## Final package layout + +``` +com.getpcpanel +├── Main # @QuarkusMain entry point +├── commands/ # the command ENGINE (provides no commands itself) +│ ├── command/ # Command, Dial/Button/DeviceAction SPIs, CommandNoOp, CommandConverter, +│ │ # CommandValueOutput (shared output base), DialValue + DialValueCalculator +│ ├── meta/ # @CommandMeta, CommandKind, CommandCategory +│ ├── CommandModule, CommandSubtypeRegistrar # the decentralized type-registry SPI +│ ├── CommandDispatcher, Commands, IconService, IIconHandler, KeyMacro-free … +│ +├── integration/ # EVERY command-providing feature, each self-contained +│ ├── volume/ # command/ + the volume feature's whole stack: +│ │ ├── command/ , mutecolor/ , overlay/ +│ │ ├── VolumeCoordinatorService, FocusVolumeOverrideService, AudioResource, OverlayResource, … +│ │ └── platform/ # the OS audio facade ISndCtrl + per-OS backends +│ │ ├── (facade + shared types: AudioDevice/Session(+Event), DataFlow, EventType, MuteType, Role) +│ │ ├── windows/ (SndCtrlWindows, SndCtrlNative, WindowsAudio*) osx/ (SndCtrlOsx, CoreAudio*) +│ │ └── linux/ (SndCtrlPulseAudio, PulseAudio*) +│ ├── keyboard/ # command/ + Keyboard interface (build-selected) + platform/{windows,osx,linux} +│ ├── program/ # command/ + IPlatformCommand (per-OS exec/kill backend) +│ ├── device/ # ONLY the brightness command module (command/) — the device HAL is separate +│ ├── analogbands/ profile/ # former "core" command families +│ └── obs/ voicemeeter/ wavelink/ discord/ homeassistant/ mqtt/ osc/ # external connectors +│ # each: command/(+CommandModule), rest/(+dto), Mute/Icon SPI impls, service/client +│ +├── device/ # device HARDWARE-ABSTRACTION LAYER (not an integration — no commands) +│ ├── Device*, DeviceFactory, GenericDevice, DeviceType, DescriptorFactory, DeviceHolder +│ ├── descriptor/ # DeviceDescriptor + Spec records (the TS device contract) +│ ├── provider/ # DeviceProvider/Registry SPI + pcpanel/ (the HID provider) + deej/ + midi/ +│ ├── rest/ # DeviceResource, SerialResource, MidiResource (device-management API) +│ ├── BrightnessService, ProVisualColorsService # queried by the HAL's OutputInterpreter +│ +├── profile/ # persistence: Save, SaveService, Profile, deserializers, profile-switching +├── sleepdetection/ # OS sleep/wake/lock monitoring (drives device lighting) — per-OS impls +├── iconextract/ # app-icon extraction SPI + per-OS impls (shared: app/process picker) +├── rest/ # SHARED web bridge: Settings/Platform/System/Icon/Process resources, +│ # EventWebSocket, EventBroadcaster, LocalHttpGuard, model/{dto,ws} +├── platform/ # build-time CDI stereotypes (@WindowsBuild/@LinuxBuild/@MacBuild) + +│ └── process/ # the shared foreground-window/process helpers (3+ features use them) +├── graalvm/ # NativeImageConfig, JnaWin32ReflectionConfig (native-image registration) +└── util/ # shared infrastructure, now grouped: + ├── image/ concurrent/ os/ io/ app/ # grouped helper clusters + ├── coloroverride/ tray/ version/ # existing subpackages + └── CdiHelper, Util, SharedHttpClient, ValueInterpolator # ubiquitous primitives at root +``` + +`src/main/cpp/` (the C++ source of `SndCtrl.dll`) is unchanged in location; only the **Java** +`com.getpcpanel.cpp` package was dissolved. `dev.niels.{discord,wavelink}` (low-level RPC clients) keep +their separate namespace. + +## Key design patterns + +### Decentralized command registry — no central list anywhere +`Command` uses `@JsonTypeInfo(Id.NAME)` with **no** `@JsonSubTypes`. Each concrete command declares its +own stable `@JsonTypeName` id (a nice id like `voicemeeter.advanced`) *in its own file* and carries +`@CommandMeta(label, category, kinds, integration, icon, legacyIds)`. Each feature owns an +`@ApplicationScoped CommandModule` bean listing only its own commands. `CommandSubtypeRegistrar` +(an `ObjectMapperCustomizer`) collects every module via CDI `@All` and registers them with Jackson. +**Adding a command or a whole feature touches nothing outside its package.** + +Backwards compatibility: `@CommandMeta.legacyIds` records the previous `_type` (the old FQCN); a +`DeserializationProblemHandler` maps an unknown legacy id back to its command on read only, so old +`profiles.json` keep loading and re-saving rewrites them with the nice id. + +The frontend registry is **generated from Java**: `CommandRegistryGeneratorTest` emits +`command-registry.generated.ts` from the `@CommandMeta` annotations (and guards staleness); +`command-catalog.ts` consumes it and keeps only the hand-written field editors. + +### Build-selected platform interfaces (no caller-side OS branching) +Platform behaviour sits behind an interface with one implementation per OS, chosen by the +`@WindowsBuild`/`@MacBuild`/`@LinuxBuild` CDI stereotypes (which wrap `@IfBuildProperty` on +`pcpanel.build.os`). Callers inject the interface and never check the OS; the impls are package-private. +Examples: `ISndCtrl` (audio) and `Keyboard` (keystroke + media — the per-OS `WindowsKeyboard`/ +`OsxKeyboard`/`LinuxKeyboard` are hidden behind it; there is no public platform-keyboard class and no +`KeyMacro`-style platform switch). + +### The device HAL and providers are not integrations +`com.getpcpanel.device` is the hardware-abstraction layer: the `Device` model, `DeviceDescriptor` +contract, `DeviceHolder` registry, and the `DeviceProvider` framework with the `pcpanel` (HID), `deej` +and `midi` providers. Provider input is normalized to the canonical 0–255 domain at the edge. None of +this provides commands, so none of it is under `integration/` — only the brightness *command* module is +(`integration/device/command`). + +### Native-image registration by `.class`, not String +Project classes that need reflection registration are referenced by `.class` in the +`@RegisterForReflection(targets = …)` of `NativeImageConfig`/`JnaWin32ReflectionConfig` — **not** by +String class name. A String name silently survives a package move and breaks only the native build; a +`.class` reference is a compile error. Required internal classes are made public enough to be referenced +this way; only genuinely third-party internals (Eclipse Paho, jna-platform) remain as String +`classNames`. The `--initialize-at-run-time` build-args (necessarily Strings) live in both `pom.xml` +and `application.properties`, kept identical by `NativeBuildArgsParityTest`. + +The Windows `SndCtrl.dll` JNI boundary is coupled to its Java package: `SndCtrlNative`'s package is +encoded in the exported `Java_…` symbol names and in the `FindClass` strings the C++ uses to build +`AudioDevice`/`AudioSession`. Moving it requires updating `src/main/cpp/` and rebuilding the DLL (the +MinGW cross-compile from Linux works — see `src/main/cpp/README.md`). + +## How to add things + +- **A command:** drop the class in its feature's `command/` package with `@JsonTypeName("feature.x")` + and `@CommandMeta(...)`, and list it in that feature's `CommandModule`. Regenerate the frontend + registry (`CommandRegistryGeneratorTest -Dpcpanel.generate.catalog`). Nothing central changes. +- **A new integration:** create `com.getpcpanel.integration.` with a `command/` package + a + `CommandModule`, and (if it has them) a `MuteStateResolver`/`IIconHandler` impl and its service/REST — + all discovered via CDI. The `**.command.**` classPattern and the `@All` SPIs pick it up automatically. +- **A new device provider:** add it under `com.getpcpanel.device.provider.` implementing + `DeviceProvider`; it is discovered via `Instance`. It is HAL, not an integration. +- **A new platform backend:** add an `@Build` implementation of the relevant interface; do not add + OS branches at call sites. + +## Guards (all green) +`CommandSubtypeRegistryTest` (every command has a unique `@JsonTypeName`; the `CommandModule` SPI covers +exactly the concrete set; every id resolves), `CommandSubtypeRegistrarTest` (registrar wiring + +legacy-id read/convert), `CommandRegistryGeneratorTest` (frontend registry current), +`ReflectionRegistrationCoverageTest` + `ProxyRegistrationCoverageTest` (native reflection/proxy coverage +across `com.getpcpanel.**`), `NativeBuildArgsParityTest` (pom ↔ application.properties build-args). + +Git history is preserved throughout via `git mv` for every relocation. diff --git a/pom.xml b/pom.xml index faec6ef1..21b742ae 100644 --- a/pom.xml +++ b/pom.xml @@ -56,12 +56,12 @@ -Os, -R:MaxHeapSize=67108864, - --initialize-at-run-time=com.getpcpanel.util.Kernel32Console, - --initialize-at-run-time=com.getpcpanel.cpp.linux.LinuxKeyboard$X11, - --initialize-at-run-time=com.getpcpanel.cpp.linux.LinuxKeyboard$XTest, - --initialize-at-run-time=com.getpcpanel.overlay.OverlayRenderer, + --initialize-at-run-time=com.getpcpanel.util.os.Kernel32Console, + --initialize-at-run-time=com.getpcpanel.integration.keyboard.platform.linux.LinuxKeyboard$X11, + --initialize-at-run-time=com.getpcpanel.integration.keyboard.platform.linux.LinuxKeyboard$XTest, + --initialize-at-run-time=com.getpcpanel.integration.volume.overlay.OverlayRenderer, --trace-object-instantiation=java.security.SecureRandom, - -J-XX:-UseCompressedOops,--initialize-at-run-time=com.sun.jna,--initialize-at-run-time=org.hid4java,--initialize-at-run-time=com.fazecast.jSerialComm,--initialize-at-run-time=com.sun.media.sound,--initialize-at-run-time=javax.sound.midi.MidiSystem,--initialize-at-run-time=com.github.kwhat.jnativehook,--initialize-at-run-time=com.getpcpanel.commands.KeyMacro,--initialize-at-run-time=com.getpcpanel.iconextract.Shell32Extra,--initialize-at-run-time=com.getpcpanel.util.tray.win.WinShell32,--initialize-at-run-time=com.getpcpanel.util.tray.win.WinUser32Ext,--initialize-at-run-time=com.getpcpanel.sleepdetection.Win32Desktop,--initialize-at-run-time=com.getpcpanel.sleepdetection.Win32PowerNotify,--initialize-at-run-time=com.getpcpanel.sleepdetection.WindowsPowerEventMonitor,--initialize-at-run-time=com.getpcpanel.sleepdetection.LinuxX11$LibX11,--initialize-at-run-time=com.getpcpanel.sleepdetection.LinuxX11$LibXext,--initialize-at-run-time=com.getpcpanel.cpp.osx.CoreAudioLib,--initialize-at-run-time=com.getpcpanel.cpp.osx.CoreAudioLib$AudioObjectPropertyAddress,--initialize-at-run-time=com.getpcpanel.cpp.osx.CoreAudioLib$AudioObjectPropertyListenerProc,--initialize-at-run-time=com.getpcpanel.cpp.osx.OsxKeyboard$CoreGraphics,--initialize-at-run-time=com.getpcpanel.cpp.osx.OsxKeyboard$CoreFoundation,--initialize-at-run-time=com.getpcpanel.util.OsxPermissionHelper,--initialize-at-run-time=com.getpcpanel.util.OsxPermissionHelper$ApplicationServicesLib,--initialize-at-run-time=com.getpcpanel.util.OsxPermissionHelper$IOKitLib,--initialize-at-run-time=org.freedesktop.dbus,--initialize-at-run-time=com.getpcpanel.voicemeeter.VoicemeeterInstance,--initialize-at-run-time=com.getpcpanel.voicemeeter.VoicemeeterInstance$tagVBVMR_AUDIOINFO,--initialize-at-run-time=com.getpcpanel.voicemeeter.VoicemeeterInstance$tagVBVMR_AUDIOBUFFER,--initialize-at-run-time=com.getpcpanel.voicemeeter.VoicemeeterInstance$tagVBVMR_INTERFACE${native.awt.args}${native.platform.linker.args} + -J-XX:-UseCompressedOops,--initialize-at-run-time=com.sun.jna,--initialize-at-run-time=org.hid4java,--initialize-at-run-time=com.fazecast.jSerialComm,--initialize-at-run-time=com.sun.media.sound,--initialize-at-run-time=javax.sound.midi.MidiSystem,--initialize-at-run-time=com.github.kwhat.jnativehook,--initialize-at-run-time=com.getpcpanel.iconextract.Shell32Extra,--initialize-at-run-time=com.getpcpanel.util.tray.win.WinShell32,--initialize-at-run-time=com.getpcpanel.util.tray.win.WinUser32Ext,--initialize-at-run-time=com.getpcpanel.sleepdetection.Win32Desktop,--initialize-at-run-time=com.getpcpanel.sleepdetection.Win32PowerNotify,--initialize-at-run-time=com.getpcpanel.sleepdetection.WindowsPowerEventMonitor,--initialize-at-run-time=com.getpcpanel.sleepdetection.LinuxX11$LibX11,--initialize-at-run-time=com.getpcpanel.sleepdetection.LinuxX11$LibXext,--initialize-at-run-time=com.getpcpanel.integration.volume.platform.osx.CoreAudioLib,--initialize-at-run-time=com.getpcpanel.integration.volume.platform.osx.CoreAudioLib$AudioObjectPropertyAddress,--initialize-at-run-time=com.getpcpanel.integration.volume.platform.osx.CoreAudioLib$AudioObjectPropertyListenerProc,--initialize-at-run-time=com.getpcpanel.integration.keyboard.platform.osx.OsxKeyboard$CoreGraphics,--initialize-at-run-time=com.getpcpanel.integration.keyboard.platform.osx.OsxKeyboard$CoreFoundation,--initialize-at-run-time=com.getpcpanel.util.os.OsxPermissionHelper,--initialize-at-run-time=com.getpcpanel.util.os.OsxPermissionHelper$ApplicationServicesLib,--initialize-at-run-time=com.getpcpanel.util.os.OsxPermissionHelper$IOKitLib,--initialize-at-run-time=org.freedesktop.dbus,--initialize-at-run-time=com.getpcpanel.integration.voicemeeter.VoicemeeterInstance,--initialize-at-run-time=com.getpcpanel.integration.voicemeeter.VoicemeeterInstance$tagVBVMR_AUDIOINFO,--initialize-at-run-time=com.getpcpanel.integration.voicemeeter.VoicemeeterInstance$tagVBVMR_AUDIOBUFFER,--initialize-at-run-time=com.getpcpanel.integration.voicemeeter.VoicemeeterInstance$tagVBVMR_INTERFACE${native.awt.args}${native.platform.linker.args} - ,--initialize-at-run-time=java.awt,--initialize-at-run-time=javax.swing,--initialize-at-run-time=sun.awt,--initialize-at-run-time=sun.lwawt,--initialize-at-run-time=sun.java2d,--initialize-at-run-time=sun.font,--initialize-at-run-time=javax.imageio,--initialize-at-run-time=com.sun.imageio,--initialize-at-run-time=com.getpcpanel.overlay.OverlayRenderer + ,--initialize-at-run-time=java.awt,--initialize-at-run-time=javax.swing,--initialize-at-run-time=sun.awt,--initialize-at-run-time=sun.lwawt,--initialize-at-run-time=sun.java2d,--initialize-at-run-time=sun.font,--initialize-at-run-time=javax.imageio,--initialize-at-run-time=com.sun.imageio,--initialize-at-run-time=com.getpcpanel.integration.volume.overlay.OverlayRenderer @@ -375,10 +375,11 @@ com.getpcpanel.rest.model.** com.getpcpanel.commands.Commands - com.getpcpanel.commands.command.** - com.getpcpanel.wavelink.command.** - com.getpcpanel.discord.command.** - com.getpcpanel.homeassistant.command.** + + com.getpcpanel.**.command.** com.getpcpanel.device.descriptor.** **.dto.** @@ -573,7 +574,7 @@ - ,--initialize-at-run-time=java.awt,--initialize-at-run-time=javax.swing,--initialize-at-run-time=sun.awt,--initialize-at-run-time=sun.lwawt,--initialize-at-run-time=sun.java2d,--initialize-at-run-time=sun.font,--initialize-at-run-time=javax.imageio,--initialize-at-run-time=com.sun.imageio,--initialize-at-run-time=com.getpcpanel.overlay.OverlayRenderer + ,--initialize-at-run-time=java.awt,--initialize-at-run-time=javax.swing,--initialize-at-run-time=sun.awt,--initialize-at-run-time=sun.lwawt,--initialize-at-run-time=sun.java2d,--initialize-at-run-time=sun.font,--initialize-at-run-time=javax.imageio,--initialize-at-run-time=com.sun.imageio,--initialize-at-run-time=com.getpcpanel.integration.volume.overlay.OverlayRenderer diff --git a/src/main/cpp/CMakeLists.txt b/src/main/cpp/CMakeLists.txt index 8ba755ac..99d938c4 100644 --- a/src/main/cpp/CMakeLists.txt +++ b/src/main/cpp/CMakeLists.txt @@ -60,7 +60,7 @@ set(SNDCTRL_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/SndCtrl) add_library(SndCtrl SHARED ${SNDCTRL_SRC_DIR}/AudioDevice.cpp ${SNDCTRL_SRC_DIR}/AudioSession.cpp - ${SNDCTRL_SRC_DIR}/com_getpcpanel_cpp_windows_SndCtrlNative.cpp + ${SNDCTRL_SRC_DIR}/com_getpcpanel_integration_volume_platform_windows_SndCtrlNative.cpp ${SNDCTRL_SRC_DIR}/dllmain.cpp ${SNDCTRL_SRC_DIR}/FocusListener.cpp ${SNDCTRL_SRC_DIR}/helpers.cpp diff --git a/src/main/cpp/README.md b/src/main/cpp/README.md index 66bad811..c4dac0fd 100644 --- a/src/main/cpp/README.md +++ b/src/main/cpp/README.md @@ -116,7 +116,7 @@ functionally unchanged: There is no automated runtime test (it needs real audio hardware and a JVM). After building, sanity-check the export surface — it must match the committed -DLL exactly (11 `Java_com_getpcpanel_cpp_windows_SndCtrlNative_*` entry points +DLL exactly (11 `Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_*` entry points plus `JNI_OnLoad` / `JNI_OnUnload`): ```powershell diff --git a/src/main/cpp/SndCtrl/AudioSession.cpp b/src/main/cpp/SndCtrl/AudioSession.cpp index 6a6b70e0..6d5045f8 100644 --- a/src/main/cpp/SndCtrl/AudioSession.cpp +++ b/src/main/cpp/SndCtrl/AudioSession.cpp @@ -53,7 +53,7 @@ void AudioSession::Init(JniCaller& audioDevice, AudioSessionListenerCB& callback auto pNameStr = thread.jstr(pname.c_str()); auto iconStr = thread.jstr(icon); jlong pointer = reinterpret_cast(this); - auto jObj = audioDevice.CallObject(thread, "addSession", "(JILjava/lang/String;Ljava/lang/String;Ljava/lang/String;FZ)Lcom/getpcpanel/cpp/AudioSession;", + auto jObj = audioDevice.CallObject(thread, "addSession", "(JILjava/lang/String;Ljava/lang/String;Ljava/lang/String;FZ)Lcom/getpcpanel/integration/volume/platform/AudioSession;", pointer, pid, nameStr, pNameStr, iconStr, level, muted ); thread.jstr(nameStr); diff --git a/src/main/cpp/SndCtrl/Listeners.h b/src/main/cpp/SndCtrl/Listeners.h index 52f4cece..345ee027 100644 --- a/src/main/cpp/SndCtrl/Listeners.h +++ b/src/main/cpp/SndCtrl/Listeners.h @@ -273,7 +273,7 @@ class AudioSessionListener : public Listener, public IAudioSessionEvents { JThread env; if (*env) { auto dnStr = env.jstr(NewDisplayName); - jni.CallObject(env, "name", "(Ljava/lang/String;)Lcom/getpcpanel/cpp/AudioSession;", dnStr); + jni.CallObject(env, "name", "(Ljava/lang/String;)Lcom/getpcpanel/integration/volume/platform/AudioSession;", dnStr); env.jstr(dnStr); } return S_OK; @@ -282,7 +282,7 @@ class AudioSessionListener : public Listener, public IAudioSessionEvents { JThread env; if (*env) { auto iconStr = env.jstr(NewIconPath); - jni.CallObject(env, "icon", "(Ljava/lang/String;)Lcom/getpcpanel/cpp/AudioSession;", iconStr); + jni.CallObject(env, "icon", "(Ljava/lang/String;)Lcom/getpcpanel/integration/volume/platform/AudioSession;", iconStr); env.jstr(iconStr); } return S_OK; @@ -293,8 +293,8 @@ class AudioSessionListener : public Listener, public IAudioSessionEvents { virtual HRESULT STDMETHODCALLTYPE OnSimpleVolumeChanged(float NewVolume, BOOL NewMute, LPCGUID EventContext) { JThread env; if (*env) { - jni.CallObject(env, "volume", "(F)Lcom/getpcpanel/cpp/AudioSession;", NewVolume); - jni.CallObject(env, "muted", "(Z)Lcom/getpcpanel/cpp/AudioSession;", NewMute); + jni.CallObject(env, "volume", "(F)Lcom/getpcpanel/integration/volume/platform/AudioSession;", NewVolume); + jni.CallObject(env, "muted", "(Z)Lcom/getpcpanel/integration/volume/platform/AudioSession;", NewMute); } return S_OK; } diff --git a/src/main/cpp/SndCtrl/SndCtrl.vcxproj b/src/main/cpp/SndCtrl/SndCtrl.vcxproj index 9eb15459..8431f69d 100644 --- a/src/main/cpp/SndCtrl/SndCtrl.vcxproj +++ b/src/main/cpp/SndCtrl/SndCtrl.vcxproj @@ -158,7 +158,7 @@ - + @@ -171,7 +171,7 @@ - + diff --git a/src/main/cpp/SndCtrl/SndCtrl.vcxproj.filters b/src/main/cpp/SndCtrl/SndCtrl.vcxproj.filters index 43e99e1e..d7b3627d 100644 --- a/src/main/cpp/SndCtrl/SndCtrl.vcxproj.filters +++ b/src/main/cpp/SndCtrl/SndCtrl.vcxproj.filters @@ -45,7 +45,7 @@ Header Files - + Header Files @@ -59,7 +59,7 @@ Source Files - + Source Files diff --git a/src/main/cpp/SndCtrl/com_getpcpanel_cpp_windows_SndCtrlNative.h b/src/main/cpp/SndCtrl/com_getpcpanel_cpp_windows_SndCtrlNative.h deleted file mode 100644 index b55c5e15..00000000 --- a/src/main/cpp/SndCtrl/com_getpcpanel_cpp_windows_SndCtrlNative.h +++ /dev/null @@ -1,109 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class com_getpcpanel_cpp_windows_SndCtrlNative */ - -#ifndef _Included_com_getpcpanel_cpp_windows_SndCtrlNative -#define _Included_com_getpcpanel_cpp_windows_SndCtrlNative -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: com_getpcpanel_cpp_windows_SndCtrlNative - * Method: start - * Signature: (Ljava/lang/Object;)V - */ -JNIEXPORT void JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_start - (JNIEnv *, jclass, jobject); - -/* - * Class: com_getpcpanel_cpp_windows_SndCtrlNative - * Method: setDeviceVolume - * Signature: (Ljava/lang/String;F)V - */ -JNIEXPORT void JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_setDeviceVolume - (JNIEnv *, jobject, jstring, jfloat); - -/* - * Class: com_getpcpanel_cpp_windows_SndCtrlNative - * Method: setProcessVolume - * Signature: (Ljava/lang/String;IF)V - */ -JNIEXPORT void JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_setProcessVolume - (JNIEnv *, jobject, jstring, jint, jfloat); - -/* - * Class: com_getpcpanel_cpp_windows_SndCtrlNative - * Method: setFocusVolume - * Signature: (F)V - */ -JNIEXPORT void JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_setFocusVolume - (JNIEnv *, jobject, jfloat); - -/* - * Class: com_getpcpanel_cpp_windows_SndCtrlNative - * Method: setDefaultDevice - * Signature: (Ljava/lang/String;II)V - */ -JNIEXPORT void JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_setDefaultDevice - (JNIEnv *, jobject, jstring, jint, jint); - -/* - * Class: com_getpcpanel_cpp_windows_SndCtrlNative - * Method: muteDevice - * Signature: (Ljava/lang/String;Z)V - */ -JNIEXPORT void JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_muteDevice - (JNIEnv *, jobject, jstring, jboolean); - -/* - * Class: com_getpcpanel_cpp_windows_SndCtrlNative - * Method: muteSession - * Signature: (Ljava/lang/String;IZ)V - */ -JNIEXPORT void JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_muteSession - (JNIEnv *, jobject, jstring, jint, jboolean); - -/* - * Class: com_getpcpanel_cpp_windows_SndCtrlNative - * Method: getFocusApplication - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_getFocusApplication - (JNIEnv *, jobject); - -/* - * Class: com_getpcpanel_cpp_windows_SndCtrlNative - * Method: setPersistedDefaultAudioEndpoint - * Signature: (IILjava/lang/String;)Z - */ -JNIEXPORT jboolean JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_setPersistedDefaultAudioEndpoint -(JNIEnv*, jobject, jint, jint, jstring); - -/* - * Class: com_getpcpanel_cpp_windows_SndCtrlNative - * Method: getPersistedDefaultAudioEndpoint - * Signature: (II)Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_getPersistedDefaultAudioEndpoint -(JNIEnv*, jobject, jint, jint); - -/* - * Class: com_getpcpanel_cpp_windows_SndCtrlNative - * Method: hasAudioPolicyConfigFactory - * Signature: ()Z - */ -JNIEXPORT jboolean JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_hasAudioPolicyConfigFactory -(JNIEnv*, jobject); - -/* - * Class: com_getpcpanel_cpp_windows_SndCtrlNative - * Method: triggerAv - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_triggerAv -(JNIEnv*, jobject); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/main/cpp/SndCtrl/com_getpcpanel_cpp_windows_SndCtrlNative.cpp b/src/main/cpp/SndCtrl/com_getpcpanel_integration_volume_platform_windows_SndCtrlNative.cpp similarity index 54% rename from src/main/cpp/SndCtrl/com_getpcpanel_cpp_windows_SndCtrlNative.cpp rename to src/main/cpp/SndCtrl/com_getpcpanel_integration_volume_platform_windows_SndCtrlNative.cpp index 9c1b59bf..b0b3a148 100644 --- a/src/main/cpp/SndCtrl/com_getpcpanel_cpp_windows_SndCtrlNative.cpp +++ b/src/main/cpp/SndCtrl/com_getpcpanel_integration_volume_platform_windows_SndCtrlNative.cpp @@ -1,5 +1,5 @@ #include "pch.h" -#include "com_getpcpanel_cpp_windows_SndCtrlNative.h" +#include "com_getpcpanel_integration_volume_platform_windows_SndCtrlNative.h" #include "JniCaller.h" #include "sndctrl.h" #include "helpers.h" @@ -25,11 +25,11 @@ std::wstring str(JNIEnv* env, jstring string) { } /* - * Class: com_getpcpanel_cpp_SndCtrlNative + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative * Method: start * Signature: (Ljava/lang/Object;)V */ -JNIEXPORT void JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_start(JNIEnv* env, jclass, jobject obj) { +JNIEXPORT void JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_start(JNIEnv* env, jclass, jobject obj) { // Build SndCtrl on a dedicated thread so this JNI call returns immediately. Its constructor // enumerates every audio endpoint (InitDevices) with synchronous COM calls that can block // indefinitely on an endpoint that is slow or stuck re-initialising (e.g. a virtual device right @@ -58,126 +58,126 @@ JNIEXPORT void JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_start(JNIEn } /* - * Class: com_getpcpanel_cpp_SndCtrlNative + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative * Method: setDeviceVolume * Signature: (Ljava/lang/String;F)V */ -JNIEXPORT void JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_setDeviceVolume(JNIEnv* env, jobject, jstring jDeviceId, jfloat volume) { - //cout << "Java_com_getpcpanel_cpp_SndCtrlNative_setDeviceVolume" << endl; +JNIEXPORT void JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_setDeviceVolume(JNIEnv* env, jobject, jstring jDeviceId, jfloat volume) { + //cout << "Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_setDeviceVolume" << endl; if (!pSndCtrl) return; auto deviceId = str(env, jDeviceId); pSndCtrl->SetDeviceVolume(deviceId, volume); } /* - * Class: com_getpcpanel_cpp_SndCtrlNative + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative * Method: setProcessVolume * Signature: (Ljava/lang/String;IF)V */ -JNIEXPORT void JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_setProcessVolume(JNIEnv* env, jobject, jstring jDeviceId, jint pid, jfloat volume) { - //cout << "Java_com_getpcpanel_cpp_SndCtrlNative_setProcessVolume" << endl; +JNIEXPORT void JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_setProcessVolume(JNIEnv* env, jobject, jstring jDeviceId, jint pid, jfloat volume) { + //cout << "Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_setProcessVolume" << endl; if (!pSndCtrl) return; auto deviceId = str(env, jDeviceId); pSndCtrl->SetProcessVolume(deviceId, pid, volume); } /* - * Class: com_getpcpanel_cpp_SndCtrlNative + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative * Method: setFocusVolume * Signature: (F)V */ -JNIEXPORT void JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_setFocusVolume(JNIEnv*, jobject, jfloat volume) { - //cout << "Java_com_getpcpanel_cpp_SndCtrlNative_setFocusVolume" << endl; +JNIEXPORT void JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_setFocusVolume(JNIEnv*, jobject, jfloat volume) { + //cout << "Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_setFocusVolume" << endl; if (!pSndCtrl) return; pSndCtrl->SetFocusVolume(volume); } /* - * Class: com_getpcpanel_cpp_SndCtrlNative + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative * Method: setDefaultDevice * Signature: (Ljava/lang/String;)V */ -JNIEXPORT void JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_setDefaultDevice(JNIEnv* env, jobject, jstring jDevice, jint dataFlow, jint role) { - //cout << "Java_com_getpcpanel_cpp_SndCtrlNative_setDefaultDevice" << endl; +JNIEXPORT void JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_setDefaultDevice(JNIEnv* env, jobject, jstring jDevice, jint dataFlow, jint role) { + //cout << "Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_setDefaultDevice" << endl; if (!pSndCtrl) return; auto device = str(env, jDevice); pSndCtrl->UpdateDefaultDevice(device, (EDataFlow) dataFlow, (ERole) role); } /* - * Class: com_getpcpanel_cpp_SndCtrlNative + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative * Method: muteDevice * Signature: (Ljava/lang/String;)V */ -JNIEXPORT void JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_muteDevice(JNIEnv* env, jobject, jstring jDevice, jboolean muted) { - //cout << "Java_com_getpcpanel_cpp_SndCtrlNative_muteDevice" << endl; +JNIEXPORT void JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_muteDevice(JNIEnv* env, jobject, jstring jDevice, jboolean muted) { + //cout << "Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_muteDevice" << endl; if (!pSndCtrl) return; auto device = str(env, jDevice); pSndCtrl->MuteDevice(device, muted); } /* - * Class: com_getpcpanel_cpp_SndCtrlNative + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative * Method: muteSession * Signature: (Ljava/lang/String;I)V */ -JNIEXPORT void JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_muteSession(JNIEnv* env, jobject, jstring jDevice, jint pid, jboolean muted) { - //cout << "Java_com_getpcpanel_cpp_SndCtrlNative_muteSession" << endl; +JNIEXPORT void JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_muteSession(JNIEnv* env, jobject, jstring jDevice, jint pid, jboolean muted) { + //cout << "Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_muteSession" << endl; if (!pSndCtrl) return; auto device = str(env, jDevice); pSndCtrl->MuteProcess(device, pid, muted); } /* - * Class: com_getpcpanel_cpp_SndCtrlNative + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative * Method: getFocusApplication * Signature: ()Ljava/lang/String; */ -JNIEXPORT jstring JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_getFocusApplication(JNIEnv* env, jobject) { - //cout << "Java_com_getpcpanel_cpp_SndCtrlNative_getFocusApplication" << endl; +JNIEXPORT jstring JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_getFocusApplication(JNIEnv* env, jobject) { + //cout << "Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_getFocusApplication" << endl; auto pid = GetFocusProcessId(); auto name = GetProcessName(pid); return env->NewString((jchar*) name.c_str(), (jsize) name.length()); } /* - * Class: com_getpcpanel_cpp_windows_SndCtrlNative + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative * Method: setPersistedDefaultAudioEndpoint * Signature: (IILjava/lang/String;)Z */ -JNIEXPORT jboolean JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_setPersistedDefaultAudioEndpoint(JNIEnv* env, jobject, jint pid, jint flow, jstring jDeviceId) { +JNIEXPORT jboolean JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_setPersistedDefaultAudioEndpoint(JNIEnv* env, jobject, jint pid, jint flow, jstring jDeviceId) { if (!pSndCtrl) return false; auto device = str(env, jDeviceId); return pSndCtrl->SetPersistedDefaultAudioEndpoint(pid, (EDataFlow) flow, device); } /* - * Class: com_getpcpanel_cpp_windows_SndCtrlNative + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative * Method: getPersistedDefaultAudioEndpoint * Signature: (II)Ljava/lang/String; */ -JNIEXPORT jstring JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_getPersistedDefaultAudioEndpoint(JNIEnv* env, jobject, jint pid, jint flow) { +JNIEXPORT jstring JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_getPersistedDefaultAudioEndpoint(JNIEnv* env, jobject, jint pid, jint flow) { if (!pSndCtrl) return env->NewString((jchar*) L"", 0); auto result = pSndCtrl->GetPersistedDefaultAudioEndpoint(pid, (EDataFlow) flow); return env->NewString((jchar*) result.c_str(), (jsize)result.length()); } /* - * Class: com_getpcpanel_cpp_windows_SndCtrlNative + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative * Method: hasAudioPolicyConfigFactory * Signature: ()Z */ -JNIEXPORT jboolean JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_hasAudioPolicyConfigFactory(JNIEnv*, jobject) { +JNIEXPORT jboolean JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_hasAudioPolicyConfigFactory(JNIEnv*, jobject) { if (!pSndCtrl) return false; return pSndCtrl->HasAudioPolicyConfigFactory(); } /* - * Class: com_getpcpanel_cpp_windows_SndCtrlNative + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative * Method: triggerAv * Signature: ()V */ -JNIEXPORT void JNICALL Java_com_getpcpanel_cpp_windows_SndCtrlNative_triggerAv(JNIEnv*, jobject) { +JNIEXPORT void JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_triggerAv(JNIEnv*, jobject) { new std::thread([](){ Sleep(1000); if (pSndCtrl) pSndCtrl->TriggerAv(); diff --git a/src/main/cpp/SndCtrl/com_getpcpanel_integration_volume_platform_windows_SndCtrlNative.h b/src/main/cpp/SndCtrl/com_getpcpanel_integration_volume_platform_windows_SndCtrlNative.h new file mode 100644 index 00000000..1a4c2731 --- /dev/null +++ b/src/main/cpp/SndCtrl/com_getpcpanel_integration_volume_platform_windows_SndCtrlNative.h @@ -0,0 +1,109 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class com_getpcpanel_integration_volume_platform_windows_SndCtrlNative */ + +#ifndef _Included_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative +#define _Included_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative + * Method: start + * Signature: (Ljava/lang/Object;)V + */ +JNIEXPORT void JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_start + (JNIEnv *, jclass, jobject); + +/* + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative + * Method: setDeviceVolume + * Signature: (Ljava/lang/String;F)V + */ +JNIEXPORT void JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_setDeviceVolume + (JNIEnv *, jobject, jstring, jfloat); + +/* + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative + * Method: setProcessVolume + * Signature: (Ljava/lang/String;IF)V + */ +JNIEXPORT void JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_setProcessVolume + (JNIEnv *, jobject, jstring, jint, jfloat); + +/* + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative + * Method: setFocusVolume + * Signature: (F)V + */ +JNIEXPORT void JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_setFocusVolume + (JNIEnv *, jobject, jfloat); + +/* + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative + * Method: setDefaultDevice + * Signature: (Ljava/lang/String;II)V + */ +JNIEXPORT void JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_setDefaultDevice + (JNIEnv *, jobject, jstring, jint, jint); + +/* + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative + * Method: muteDevice + * Signature: (Ljava/lang/String;Z)V + */ +JNIEXPORT void JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_muteDevice + (JNIEnv *, jobject, jstring, jboolean); + +/* + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative + * Method: muteSession + * Signature: (Ljava/lang/String;IZ)V + */ +JNIEXPORT void JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_muteSession + (JNIEnv *, jobject, jstring, jint, jboolean); + +/* + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative + * Method: getFocusApplication + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_getFocusApplication + (JNIEnv *, jobject); + +/* + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative + * Method: setPersistedDefaultAudioEndpoint + * Signature: (IILjava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_setPersistedDefaultAudioEndpoint +(JNIEnv*, jobject, jint, jint, jstring); + +/* + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative + * Method: getPersistedDefaultAudioEndpoint + * Signature: (II)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_getPersistedDefaultAudioEndpoint +(JNIEnv*, jobject, jint, jint); + +/* + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative + * Method: hasAudioPolicyConfigFactory + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_hasAudioPolicyConfigFactory +(JNIEnv*, jobject); + +/* + * Class: com_getpcpanel_integration_volume_platform_windows_SndCtrlNative + * Method: triggerAv + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_triggerAv +(JNIEnv*, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/main/cpp/SndCtrl/sndctrl.cpp b/src/main/cpp/SndCtrl/sndctrl.cpp index ca22b52c..e127fab4 100644 --- a/src/main/cpp/SndCtrl/sndctrl.cpp +++ b/src/main/cpp/SndCtrl/sndctrl.cpp @@ -85,7 +85,7 @@ void SndCtrl::DeviceAdded(CComPtr cpDevice) { auto nameStr = thread.jstr(nameAndId.name.get()); auto idStr = thread.jstr(nameAndId.id.get()); auto dataFlow = getDataFlow(*cpDevice); - auto jObj = pJni->CallObject(thread, "deviceAdded", "(Ljava/lang/String;Ljava/lang/String;FZI)Lcom/getpcpanel/cpp/AudioDevice;", + auto jObj = pJni->CallObject(thread, "deviceAdded", "(Ljava/lang/String;Ljava/lang/String;FZI)Lcom/getpcpanel/integration/volume/platform/AudioDevice;", nameStr, idStr, volume, muted, dataFlow ); NULLRETURN(jObj); diff --git a/src/main/cpp/SndCtrlTest/SndCtrlTest.cpp b/src/main/cpp/SndCtrlTest/SndCtrlTest.cpp index 48e8ca14..5660a21e 100644 --- a/src/main/cpp/SndCtrlTest/SndCtrlTest.cpp +++ b/src/main/cpp/SndCtrlTest/SndCtrlTest.cpp @@ -4,11 +4,11 @@ #include #include "..\SndCtrl\pch.h" #include "..\SndCtrl\sndctrl.h" -#include "..\SndCtrl\com_getpcpanel_cpp_windows_SndCtrlNative.h" +#include "..\SndCtrl\com_getpcpanel_integration_volume_platform_windows_SndCtrlNative.h" int main() { - //Java_com_getpcpanel_cpp_SndCtrlNative_addAllRunningProcesses(nullptr, nullptr, nullptr); + //Java_com_getpcpanel_integration_volume_platform_windows_SndCtrlNative_addAllRunningProcesses(nullptr, nullptr, nullptr); std::cout << "Hello World!\n"; auto sndctrl = make_unique(nullptr, nullptr); diff --git a/src/main/cpp/SndCtrlTest/SndCtrlTest.vcxproj b/src/main/cpp/SndCtrlTest/SndCtrlTest.vcxproj index 38cbd6f2..e2506f48 100644 --- a/src/main/cpp/SndCtrlTest/SndCtrlTest.vcxproj +++ b/src/main/cpp/SndCtrlTest/SndCtrlTest.vcxproj @@ -145,7 +145,7 @@ - + @@ -159,7 +159,7 @@ - + diff --git a/src/main/cpp/SndCtrlTest/SndCtrlTest.vcxproj.filters b/src/main/cpp/SndCtrlTest/SndCtrlTest.vcxproj.filters index 2d8069fe..55f85cc5 100644 --- a/src/main/cpp/SndCtrlTest/SndCtrlTest.vcxproj.filters +++ b/src/main/cpp/SndCtrlTest/SndCtrlTest.vcxproj.filters @@ -45,7 +45,7 @@ Source Files - + Source Files @@ -77,7 +77,7 @@ Header Files - + Header Files diff --git a/src/main/cpp/header.bat b/src/main/cpp/header.bat index 669b2887..c34fe21f 100644 --- a/src/main/cpp/header.bat +++ b/src/main/cpp/header.bat @@ -1,3 +1,3 @@ @echo off -javac -h ..\cpp\SndCtrl\ ..\java\com\getpcpanel\cpp\windows\SndCtrlNative.java -del ..\java\com\getpcpanel\cpp\windows\SndCtrlNative.class +javac -h ..\cpp\SndCtrl\ ..\java\com\getpcpanel\integration\volume\platform\windows\SndCtrlNative.java +del ..\java\com\getpcpanel\integration\volume\platform\windows\SndCtrlNative.class diff --git a/src/main/java/com/getpcpanel/Main.java b/src/main/java/com/getpcpanel/Main.java index 4226db65..96badc3e 100644 --- a/src/main/java/com/getpcpanel/Main.java +++ b/src/main/java/com/getpcpanel/Main.java @@ -6,10 +6,10 @@ import org.eclipse.microprofile.config.ConfigProvider; -import com.getpcpanel.hid.HidDebug; -import com.getpcpanel.util.ConsoleSupport; -import com.getpcpanel.util.FileChecker; -import com.getpcpanel.util.PcPanelRoot; +import com.getpcpanel.device.provider.pcpanel.HidDebug; +import com.getpcpanel.util.os.ConsoleSupport; +import com.getpcpanel.util.io.FileChecker; +import com.getpcpanel.util.io.PcPanelRoot; import io.quarkus.runtime.Quarkus; import io.quarkus.runtime.QuarkusApplication; diff --git a/src/main/java/com/getpcpanel/commands/CommandModule.java b/src/main/java/com/getpcpanel/commands/CommandModule.java new file mode 100644 index 00000000..e631d4d6 --- /dev/null +++ b/src/main/java/com/getpcpanel/commands/CommandModule.java @@ -0,0 +1,18 @@ +package com.getpcpanel.commands; + +import java.util.List; + +import com.getpcpanel.commands.command.Command; + +/** + * A feature module's contribution of assignable command types. + * + *

Each module is an {@code @ApplicationScoped} bean that lives in its own feature package + * and lists only the {@link Command} subclasses it owns. {@link CommandSubtypeRegistrar} collects every + * {@code CommandModule} via CDI {@code @All} and registers the classes with Jackson (each class carries + * its own {@code @JsonTypeName}). Adding a command — or an entire new feature/plugin — therefore never + * touches anything outside the feature's package: drop the command class in, list it here, done. + */ +public interface CommandModule { + List> commandTypes(); +} diff --git a/src/main/java/com/getpcpanel/commands/CommandSubtypeRegistrar.java b/src/main/java/com/getpcpanel/commands/CommandSubtypeRegistrar.java new file mode 100644 index 00000000..9df5fb25 --- /dev/null +++ b/src/main/java/com/getpcpanel/commands/CommandSubtypeRegistrar.java @@ -0,0 +1,74 @@ +package com.getpcpanel.commands; + +import java.util.HashMap; +import java.util.List; + +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; +import com.getpcpanel.commands.command.Command; +import com.getpcpanel.commands.meta.CommandMeta; + +import io.quarkus.arc.All; +import io.quarkus.jackson.ObjectMapperCustomizer; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import lombok.extern.log4j.Log4j2; + +/** + * Registers every feature module's command classes as Jackson polymorphic subtypes of + * {@link Command}. The subtypes are discovered entirely through the {@link CommandModule} CDI SPI + * ({@code @All}), so no central list of command classes exists anywhere — a new command/plugin only + * declares itself in its own package. Jackson reads each class's {@code @JsonTypeName} for the + * (nice, current) persisted id. + * + *

Backwards compatibility. A command's {@code @CommandMeta.legacyIds} list the {@code _type} + * strings older {@code profiles.json} files may contain (their previous fully-qualified class names). + * These are not registered as subtypes (that would risk them being chosen for serialization); + * instead a {@link DeserializationProblemHandler} maps an unknown legacy id back to its command on + * read only. So existing saves keep loading, and re-saving rewrites them with the nice current id — + * a transparent one-way conversion. + */ +@Log4j2 +@Singleton +public class CommandSubtypeRegistrar implements ObjectMapperCustomizer { + @Inject + @All + List modules; + + @Override + public void customize(ObjectMapper mapper) { + var classes = modules.stream().flatMap(m -> m.commandTypes().stream()).distinct().toList(); + var legacy = new HashMap>(); + for (var clazz : classes) { + mapper.registerSubtypes(clazz); // current id via @JsonTypeName + var meta = clazz.getAnnotation(CommandMeta.class); + if (meta != null) { + for (var id : meta.legacyIds()) { + var prev = legacy.put(id, clazz); + if (prev != null && prev != clazz) { + throw new IllegalStateException("Legacy command id '" + id + "' maps to both " + prev + " and " + clazz); + } + } + } + } + if (!legacy.isEmpty()) { + // Resolve a legacy _type id (a command's old FQCN) to its current class on read only. + mapper.addHandler(new DeserializationProblemHandler() { + @Override + public JavaType handleUnknownTypeId(DeserializationContext ctxt, JavaType baseType, String subTypeId, TypeIdResolver idResolver, String failureMsg) { + if (baseType.isTypeOrSubTypeOf(Command.class)) { + var clazz = legacy.get(subTypeId); + if (clazz != null) { + return ctxt.getTypeFactory().constructType(clazz); + } + } + return null; // not a known legacy id — let Jackson report the failure + } + }); + } + log.debug("Registered {} command subtypes ({} legacy aliases) from {} module(s)", classes.size(), legacy.size(), modules.size()); + } +} diff --git a/src/main/java/com/getpcpanel/hid/DialValue.java b/src/main/java/com/getpcpanel/commands/DialValue.java similarity index 95% rename from src/main/java/com/getpcpanel/hid/DialValue.java rename to src/main/java/com/getpcpanel/commands/DialValue.java index 8613d00e..62ba6d9f 100644 --- a/src/main/java/com/getpcpanel/hid/DialValue.java +++ b/src/main/java/com/getpcpanel/commands/DialValue.java @@ -1,4 +1,4 @@ -package com.getpcpanel.hid; +package com.getpcpanel.commands; import javax.annotation.Nullable; diff --git a/src/main/java/com/getpcpanel/hid/DialValueCalculator.java b/src/main/java/com/getpcpanel/commands/DialValueCalculator.java similarity index 98% rename from src/main/java/com/getpcpanel/hid/DialValueCalculator.java rename to src/main/java/com/getpcpanel/commands/DialValueCalculator.java index c8ba90a6..67f04fa6 100644 --- a/src/main/java/com/getpcpanel/hid/DialValueCalculator.java +++ b/src/main/java/com/getpcpanel/commands/DialValueCalculator.java @@ -1,4 +1,4 @@ -package com.getpcpanel.hid; +package com.getpcpanel.commands; import static com.getpcpanel.util.Util.map; diff --git a/src/main/java/com/getpcpanel/commands/IconService.java b/src/main/java/com/getpcpanel/commands/IconService.java index 23b73346..88f58fd2 100644 --- a/src/main/java/com/getpcpanel/commands/IconService.java +++ b/src/main/java/com/getpcpanel/commands/IconService.java @@ -1,6 +1,6 @@ package com.getpcpanel.commands; -import static com.getpcpanel.cpp.AudioSession.SYSTEM; +import static com.getpcpanel.integration.volume.platform.AudioSession.SYSTEM; import java.awt.image.BufferedImage; import java.io.File; @@ -16,20 +16,18 @@ import org.apache.commons.lang3.SystemUtils; import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandBrightness; -import com.getpcpanel.commands.command.CommandObs; -import com.getpcpanel.commands.command.CommandVoiceMeeter; -import com.getpcpanel.commands.command.CommandVolumeDefaultDevice; -import com.getpcpanel.commands.command.CommandVolumeDefaultDeviceAdvanced; -import com.getpcpanel.commands.command.CommandVolumeDefaultDeviceToggle; -import com.getpcpanel.commands.command.CommandVolumeDefaultDeviceToggleAdvanced; -import com.getpcpanel.commands.command.CommandVolumeDevice; -import com.getpcpanel.commands.command.CommandVolumeFocus; -import com.getpcpanel.commands.command.CommandVolumeProcess; -import com.getpcpanel.cpp.ISndCtrl; +import com.getpcpanel.integration.device.command.CommandBrightness; +import com.getpcpanel.integration.volume.command.CommandVolumeDefaultDevice; +import com.getpcpanel.integration.volume.command.CommandVolumeDefaultDeviceAdvanced; +import com.getpcpanel.integration.volume.command.CommandVolumeDefaultDeviceToggle; +import com.getpcpanel.integration.volume.command.CommandVolumeDefaultDeviceToggleAdvanced; +import com.getpcpanel.integration.volume.command.CommandVolumeDevice; +import com.getpcpanel.integration.volume.command.CommandVolumeFocus; +import com.getpcpanel.integration.volume.command.CommandVolumeProcess; +import com.getpcpanel.integration.volume.platform.ISndCtrl; import com.getpcpanel.iconextract.IIconService; import com.getpcpanel.profile.dto.KnobSetting; -import com.getpcpanel.util.PngDecoder; +import com.getpcpanel.util.image.PngDecoder; import io.quarkus.arc.All; import io.quarkus.cache.CacheResult; @@ -43,8 +41,8 @@ @ApplicationScoped public class IconService { public BufferedImage DEFAULT; - private BufferedImage OBS; - private BufferedImage VOICEMEETER; + public BufferedImage OBS; + public BufferedImage VOICEMEETER; public BufferedImage DEVICE; public BufferedImage SYSTEM_SOUND; @@ -96,8 +94,6 @@ public void init() { // Dials imageHandlers.put(CommandVolumeProcess.class, IconService::getRunningProcessIcon); imageHandlers.put(CommandVolumeFocus.class, IconService::getFocusProcessIcon); - imageHandlers.put(CommandObs.class, IconService::getObsIcon); - imageHandlers.put(CommandVoiceMeeter.class, IconService::getVoiceMeeterIcon); imageHandlers.put(CommandVolumeDevice.class, IconService::getDeviceIcon); imageHandlers.put(CommandBrightness.class, IconService::getBrightnessIcon); @@ -188,13 +184,7 @@ private BufferedImage getDeviceIcon(Command command) { return DEVICE; } - private BufferedImage getVoiceMeeterIcon(CommandVoiceMeeter command) { - return VOICEMEETER; - } - private BufferedImage getObsIcon(CommandObs command) { - return OBS; - } private class SafeMap extends HashMap, BiFunction> { diff --git a/src/main/java/com/getpcpanel/commands/KeyMacro.java b/src/main/java/com/getpcpanel/commands/KeyMacro.java deleted file mode 100644 index 32342bd6..00000000 --- a/src/main/java/com/getpcpanel/commands/KeyMacro.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.getpcpanel.commands; - -import java.util.concurrent.atomic.AtomicBoolean; - -import org.apache.commons.lang3.SystemUtils; - -import com.getpcpanel.cpp.linux.LinuxKeyboard; -import com.getpcpanel.cpp.osx.OsxKeyboard; -import com.getpcpanel.cpp.windows.WindowsKeyboard; -import com.getpcpanel.util.OsxPermissionHelper; - -import lombok.extern.log4j.Log4j2; - -/** - * Cross-platform keystroke injection facade. Each platform uses a native (JNA) backend instead of - * {@link java.awt.Robot}, so the GraalVM native image never needs the AWT windowing toolkit: - * macOS via CoreGraphics ({@link OsxKeyboard}), Windows via {@code User32.SendInput} - * ({@link WindowsKeyboard}) and Linux via the X11 {@code XTEST} extension ({@link LinuxKeyboard}). - */ -@Log4j2 -public final class KeyMacro { - private static final AtomicBoolean accessibilityWarned = new AtomicBoolean(); - - private KeyMacro() { - } - - public static void executeKeyStroke(String input) { - if (input == null || input.contains("UNDEFINED")) - return; - if (SystemUtils.IS_OS_MAC) { - warnIfAccessibilityNotGranted(); - OsxKeyboard.executeKeyStroke(input); - } else if (SystemUtils.IS_OS_WINDOWS) { - WindowsKeyboard.executeKeyStroke(input); - } else { - LinuxKeyboard.executeKeyStroke(input); - } - } - - /** Types {@code text} out character-by-character via the platform's native keyboard backend. */ - public static void typeText(String text) { - if (text == null || text.isEmpty()) - return; - if (SystemUtils.IS_OS_MAC) { - warnIfAccessibilityNotGranted(); - OsxKeyboard.typeText(text); - } else if (SystemUtils.IS_OS_WINDOWS) { - WindowsKeyboard.typeText(text); - } else { - LinuxKeyboard.typeText(text); - } - } - - private static void warnIfAccessibilityNotGranted() { - if (SystemUtils.IS_OS_MAC && !OsxPermissionHelper.isAccessibilityGranted() && accessibilityWarned.compareAndSet(false, true)) { - log.warn("Keystrokes require Accessibility permission: System Settings > Privacy & Security > Accessibility > enable PCPanel"); - } - } -} diff --git a/src/main/java/com/getpcpanel/commands/PCPanelControlEvent.java b/src/main/java/com/getpcpanel/commands/PCPanelControlEvent.java index 54eefd2a..8340bacb 100644 --- a/src/main/java/com/getpcpanel/commands/PCPanelControlEvent.java +++ b/src/main/java/com/getpcpanel/commands/PCPanelControlEvent.java @@ -2,7 +2,7 @@ import javax.annotation.Nullable; -import com.getpcpanel.hid.DialValue; +import com.getpcpanel.commands.DialValue; import one.util.streamex.StreamEx; diff --git a/src/main/java/com/getpcpanel/commands/command/Command.java b/src/main/java/com/getpcpanel/commands/command/Command.java index 3e2b10a8..ba36fdfd 100644 --- a/src/main/java/com/getpcpanel/commands/command/Command.java +++ b/src/main/java/com/getpcpanel/commands/command/Command.java @@ -3,15 +3,30 @@ import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.getpcpanel.hid.DialValue; +import com.getpcpanel.commands.DialValue; import lombok.ToString; import lombok.extern.log4j.Log4j2; +/** + * Base type for every assignable action. + * + *

Polymorphism is fully decentralized. The discriminator uses {@link JsonTypeInfo.Id#NAME}, + * and each concrete command declares its own stable id with a {@code @JsonTypeName} in its own + * package — there is deliberately no central {@code @JsonSubTypes} list here, so adding a command + * (or a whole new feature module) never touches this class. The id is a stable, location-independent + * string equal to the command's historical fully-qualified name, so saved {@code profiles.json}, the + * generated TS {@code _type} union, and the frontend catalog are unaffected by where the class lives. + * + *

Each feature module contributes its command classes through the {@link com.getpcpanel.commands.CommandModule} + * CDI SPI; {@link com.getpcpanel.commands.CommandSubtypeRegistrar} collects them via {@code @All} and + * registers them with Jackson for deserialization. {@code typescript-generator} discovers the subtypes + * from the {@code **.command.**} class scan and reads each {@code @JsonTypeName}. + */ @Log4j2 @ToString @SuppressWarnings("InstanceofThis") -@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "_type") +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "_type") public abstract class Command { public Runnable toRunnable(boolean initial, String deviceId, @Nullable DialValue vol) { // A command that is both a dial and a button action (e.g. the generic HTTP/MQTT/OSC outputs) diff --git a/src/main/java/com/getpcpanel/commands/command/CommandConverter.java b/src/main/java/com/getpcpanel/commands/command/CommandConverter.java index 9df02904..0f99ac32 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandConverter.java +++ b/src/main/java/com/getpcpanel/commands/command/CommandConverter.java @@ -1,5 +1,17 @@ package com.getpcpanel.commands.command; +import com.getpcpanel.integration.keyboard.command.CommandKeystroke; +import com.getpcpanel.integration.keyboard.command.CommandMedia; +import com.getpcpanel.integration.profile.command.CommandProfile; +import com.getpcpanel.integration.program.command.CommandEndProgram; +import com.getpcpanel.integration.program.command.CommandShortcut; +import com.getpcpanel.integration.volume.command.CommandVolumeDefaultDevice; +import com.getpcpanel.integration.volume.command.CommandVolumeDefaultDeviceToggle; +import com.getpcpanel.integration.volume.command.CommandVolumeDevice; +import com.getpcpanel.integration.volume.command.CommandVolumeDeviceMute; +import com.getpcpanel.integration.volume.command.CommandVolumeFocus; +import com.getpcpanel.integration.volume.command.CommandVolumeProcess; +import com.getpcpanel.integration.volume.command.CommandVolumeProcessMute; import static com.getpcpanel.commands.command.CommandNoOp.NOOP; import java.util.List; @@ -8,14 +20,21 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; -import com.getpcpanel.commands.command.CommandMedia.VolumeButton; +import com.getpcpanel.integration.keyboard.command.CommandMedia.VolumeButton; import com.getpcpanel.commands.command.DialAction.DialCommandParams; -import com.getpcpanel.cpp.MuteType; -import com.getpcpanel.voicemeeter.Voicemeeter.ButtonControlMode; -import com.getpcpanel.voicemeeter.Voicemeeter.ButtonType; -import com.getpcpanel.voicemeeter.Voicemeeter.ControlType; -import com.getpcpanel.voicemeeter.Voicemeeter.DialControlMode; -import com.getpcpanel.voicemeeter.Voicemeeter.DialType; +import com.getpcpanel.integration.volume.platform.MuteType; +import com.getpcpanel.integration.obs.command.CommandObsMuteSource; +import com.getpcpanel.integration.obs.command.CommandObsSetScene; +import com.getpcpanel.integration.obs.command.CommandObsSetSourceVolume; +import com.getpcpanel.integration.voicemeeter.Voicemeeter.ButtonControlMode; +import com.getpcpanel.integration.voicemeeter.Voicemeeter.ButtonType; +import com.getpcpanel.integration.voicemeeter.Voicemeeter.ControlType; +import com.getpcpanel.integration.voicemeeter.Voicemeeter.DialControlMode; +import com.getpcpanel.integration.voicemeeter.Voicemeeter.DialType; +import com.getpcpanel.integration.voicemeeter.command.CommandVoiceMeeterAdvanced; +import com.getpcpanel.integration.voicemeeter.command.CommandVoiceMeeterAdvancedButton; +import com.getpcpanel.integration.voicemeeter.command.CommandVoiceMeeterBasic; +import com.getpcpanel.integration.voicemeeter.command.CommandVoiceMeeterBasicButton; import lombok.extern.log4j.Log4j2; import one.util.streamex.StreamEx; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandMedia.java b/src/main/java/com/getpcpanel/commands/command/CommandMedia.java deleted file mode 100644 index 6f0a8967..00000000 --- a/src/main/java/com/getpcpanel/commands/command/CommandMedia.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.getpcpanel.commands.command; - -import java.util.Map; -import java.util.Optional; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.SystemUtils; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.getpcpanel.util.CdiHelper; -import com.getpcpanel.util.OsxMediaControl; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.cpp.linux.LinuxKeyboard; -import com.getpcpanel.cpp.windows.SndCtrlWindows; -import com.sun.jna.platform.win32.BaseTSD; -import com.sun.jna.platform.win32.User32; -import com.sun.jna.platform.win32.WinDef; -import com.sun.jna.platform.win32.WinUser; -import com.sun.jna.ptr.IntByReference; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.ToString; -import lombok.extern.log4j.Log4j2; -import one.util.streamex.StreamEx; - -/** - * Spotify logic from Spotify-Controls - */ -@Getter -@Log4j2 -@ToString(callSuper = true) -public class CommandMedia extends Command implements ButtonAction { - private static final int WM_APPCOMMAND = 0x0319; - private final VolumeButton button; - private final boolean spotify; - - @RequiredArgsConstructor - public enum VolumeButton { - mute(0xAD, 0x80000), - next(0xB0, 0xB0000), - prev(0xB1, 0xC0000), - stop(0xB2, 0xD0000), - playPause(0xB3, 0xE0000); - - private final int key; - private final int spotify; - - public static Optional tryValueOf(String name) { - try { - return Optional.of(valueOf(name)); - } catch (IllegalArgumentException e) { - return Optional.empty(); - } - } - } - - @JsonCreator - public CommandMedia(@JsonProperty("button") VolumeButton button, @JsonProperty("spotify") boolean spotify) { - this.button = button; - this.spotify = spotify; - } - - @Override - public void execute() { - // Branch before the Windows paths so the win32 JNA classes are never touched off Windows - // (instantiating WinUser.INPUT throws on Linux/macOS — see Structure.getFieldOrder). - if (SystemUtils.IS_OS_MAC) { - CdiHelper.getBean(OsxMediaControl.class).execute(button, spotify); - return; - } - if (!SystemUtils.IS_OS_WINDOWS) { - // Linux/X11: the desktop routes XF86Audio* keys to the active player (incl. Spotify via - // MPRIS), so the spotify flag collapses to the same global media key. - LinuxKeyboard.sendMediaKey(button); - return; - } - if (spotify) { - executeSpotify(); - } else { - executeGlobalMedia(); - } - } - - @Override - public String buildLabel() { - return button + (spotify ? " (Spotify)" : ""); - } - - private void executeGlobalMedia() { - var input = new WinUser.INPUT(); - input.type = new WinDef.DWORD(WinUser.INPUT.INPUT_KEYBOARD); - input.input.setType("ki"); - input.input.ki.wScan = new WinDef.WORD(0); - input.input.ki.time = new WinDef.DWORD(0); - input.input.ki.dwExtraInfo = new BaseTSD.ULONG_PTR(0); - input.input.ki.wVk = new WinDef.WORD(button.key); - input.input.ki.dwFlags = new WinDef.DWORD(0); // keydown - - User32.INSTANCE.SendInput(new WinDef.DWORD(1), (WinUser.INPUT[]) input.toArray(1), input.size()); - input.input.ki.dwFlags = new WinDef.DWORD(2); // keyup - User32.INSTANCE.SendInput(new WinDef.DWORD(1), (WinUser.INPUT[]) input.toArray(1), input.size()); - } - - private void executeSpotify() { - var spotifyWnd = findSpotify(); - if (spotifyWnd == null) { - log.warn("Spotify media command: no Spotify window found, nothing sent"); - return; - } - User32.INSTANCE.SendMessage(spotifyWnd, WM_APPCOMMAND, new WinDef.WPARAM(0), new WinDef.LPARAM(button.spotify)); - } - - private WinDef.HWND findSpotify() { - var result = new WinDef.HWND[] { null }; - var apps = CdiHelper.getBean(SndCtrlWindows.class).getRunningApplications(); - var pidIsSpotify = StreamEx.of(apps).mapToEntry(ISndCtrl.RunningApplication::pid, ra -> StringUtils.equalsIgnoreCase("spotify.exe", ra.file().getName())).distinctKeys().toMap(); - var spotifyPids = pidIsSpotify.entrySet().stream().filter(Map.Entry::getValue).map(Map.Entry::getKey).toList(); - var windowsSeen = new int[] { 0 }; - User32.INSTANCE.EnumWindows((hWnd, data) -> { - windowsSeen[0]++; - var target = new IntByReference(); - User32.INSTANCE.GetWindowThreadProcessId(hWnd, target); - - if (pidIsSpotify.getOrDefault(target.getValue(), false)) { - result[0] = hWnd; - return false; - } - return true; - }, null); - log.debug("findSpotify: {} running apps, spotify pids={}, windows enumerated={}, match={}", apps.size(), spotifyPids, windowsSeen[0], result[0]); - return result[0]; - } -} diff --git a/src/main/java/com/getpcpanel/commands/command/CommandNoOp.java b/src/main/java/com/getpcpanel/commands/command/CommandNoOp.java index ee7cf80e..69e9db1f 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandNoOp.java +++ b/src/main/java/com/getpcpanel/commands/command/CommandNoOp.java @@ -1,8 +1,10 @@ package com.getpcpanel.commands.command; +import com.fasterxml.jackson.annotation.JsonTypeName; import lombok.ToString; @ToString(callSuper = true) +@JsonTypeName("com.getpcpanel.commands.command.CommandNoOp") public final class CommandNoOp extends Command implements ButtonAction, DialAction { public static final CommandNoOp NOOP = new CommandNoOp(); diff --git a/src/main/java/com/getpcpanel/commands/command/CommandObs.java b/src/main/java/com/getpcpanel/commands/command/CommandObs.java deleted file mode 100644 index 87110f15..00000000 --- a/src/main/java/com/getpcpanel/commands/command/CommandObs.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.getpcpanel.commands.command; - -import lombok.Getter; - -@Getter -public abstract class CommandObs extends Command { -} diff --git a/src/main/java/com/getpcpanel/commands/command/CommandValueOutput.java b/src/main/java/com/getpcpanel/commands/command/CommandValueOutput.java index 61f4a96a..3b5098bb 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandValueOutput.java +++ b/src/main/java/com/getpcpanel/commands/command/CommandValueOutput.java @@ -1,5 +1,8 @@ package com.getpcpanel.commands.command; +import com.getpcpanel.commands.command.Command; +import com.getpcpanel.commands.command.DialAction; +import com.getpcpanel.commands.command.ButtonAction; import javax.annotation.Nullable; import com.getpcpanel.util.ValueInterpolator; diff --git a/src/main/java/com/getpcpanel/commands/command/DialAction.java b/src/main/java/com/getpcpanel/commands/command/DialAction.java index 76695ef1..e5908eb4 100644 --- a/src/main/java/com/getpcpanel/commands/command/DialAction.java +++ b/src/main/java/com/getpcpanel/commands/command/DialAction.java @@ -2,7 +2,7 @@ import javax.annotation.Nullable; -import com.getpcpanel.hid.DialValue; +import com.getpcpanel.commands.DialValue; public interface DialAction { void execute(DialActionParameters context); diff --git a/src/main/java/com/getpcpanel/commands/command/EngineCommandModule.java b/src/main/java/com/getpcpanel/commands/command/EngineCommandModule.java new file mode 100644 index 00000000..28b7c56a --- /dev/null +++ b/src/main/java/com/getpcpanel/commands/command/EngineCommandModule.java @@ -0,0 +1,20 @@ +package com.getpcpanel.commands.command; + +import java.util.List; + +import com.getpcpanel.commands.CommandModule; + +import jakarta.enterprise.context.ApplicationScoped; + +/** + * Engine feature module: registers its own command types via the {@link com.getpcpanel.commands.CommandModule} + * SPI. Adding/removing a command touches only this package. + */ +@ApplicationScoped +public class EngineCommandModule implements CommandModule { + @Override + public List> commandTypes() { + return List.of( + CommandNoOp.class); + } +} diff --git a/src/main/java/com/getpcpanel/commands/meta/CommandCategory.java b/src/main/java/com/getpcpanel/commands/meta/CommandCategory.java new file mode 100644 index 00000000..d1be775e --- /dev/null +++ b/src/main/java/com/getpcpanel/commands/meta/CommandCategory.java @@ -0,0 +1,6 @@ +package com.getpcpanel.commands.meta; + +/** Picker grouping for a command. Matches the frontend {@code CommandCategory} union. */ +public enum CommandCategory { + audio, system, integration +} diff --git a/src/main/java/com/getpcpanel/commands/meta/CommandKind.java b/src/main/java/com/getpcpanel/commands/meta/CommandKind.java new file mode 100644 index 00000000..8568242f --- /dev/null +++ b/src/main/java/com/getpcpanel/commands/meta/CommandKind.java @@ -0,0 +1,6 @@ +package com.getpcpanel.commands.meta; + +/** Which control slot a command is valid for. Matches the frontend {@code CommandKind} union. */ +public enum CommandKind { + dial, button +} diff --git a/src/main/java/com/getpcpanel/commands/meta/CommandMeta.java b/src/main/java/com/getpcpanel/commands/meta/CommandMeta.java new file mode 100644 index 00000000..88dd3f3d --- /dev/null +++ b/src/main/java/com/getpcpanel/commands/meta/CommandMeta.java @@ -0,0 +1,45 @@ +package com.getpcpanel.commands.meta; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Declares a command as user-assignable and carries the picker/registry metadata that used to be + * hand-maintained in the frontend {@code command-catalog.ts}. A build step generates the frontend + * command registry from these annotations (see {@code docs/feature-module-structure.md}), so the + * authoritative list of which commands exist and how they are labelled/categorised/iconified lives + * in Java, next to each command, in its own feature package. + * + *

Only the registry-level metadata is here; the per-command field editors remain in the + * frontend because they are Angular UI, not data. A command without {@code @CommandMeta} still works + * (it just is not offered in the assignment picker). + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface CommandMeta { + /** Human-readable picker label. */ + String label(); + + /** Picker grouping. */ + CommandCategory category(); + + /** Control slots the command can be assigned to (dial / button). */ + CommandKind[] kinds(); + + /** Owning integration id (e.g. {@code "obs"}) for {@code category == integration}; blank for core. */ + String integration() default ""; + + /** Icon name; must be one of the frontend {@code IconName} set. */ + String icon(); + + /** + * Historical {@code _type} discriminator values older profiles.json may contain (typically the + * command's previous fully-qualified class name). The current id is the {@code @JsonTypeName}; + * these are accepted on read only, mapped back via a DeserializationProblemHandler in + * {@link com.getpcpanel.commands.CommandSubtypeRegistrar}, so old saves keep loading while new + * saves are written with the nice current id. + */ + String[] legacyIds() default {}; +} diff --git a/src/main/java/com/getpcpanel/device/Device.java b/src/main/java/com/getpcpanel/device/Device.java index 7498fe2f..e8bc769e 100644 --- a/src/main/java/com/getpcpanel/device/Device.java +++ b/src/main/java/com/getpcpanel/device/Device.java @@ -1,12 +1,14 @@ package com.getpcpanel.device; +import com.getpcpanel.device.provider.pcpanel.DeviceType; + import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import com.getpcpanel.commands.IconService; import com.getpcpanel.device.descriptor.DeviceDescriptor; -import com.getpcpanel.hid.OutputInterpreter; +import com.getpcpanel.device.provider.pcpanel.OutputInterpreter; import com.getpcpanel.profile.DeviceSave; import com.getpcpanel.profile.LightingChangedToDefaultEvent; import com.getpcpanel.profile.Profile; diff --git a/src/main/java/com/getpcpanel/hid/DeviceHolder.java b/src/main/java/com/getpcpanel/device/DeviceHolder.java similarity index 91% rename from src/main/java/com/getpcpanel/hid/DeviceHolder.java rename to src/main/java/com/getpcpanel/device/DeviceHolder.java index 4e48a4b2..386b9a56 100644 --- a/src/main/java/com/getpcpanel/hid/DeviceHolder.java +++ b/src/main/java/com/getpcpanel/device/DeviceHolder.java @@ -1,4 +1,8 @@ -package com.getpcpanel.hid; +package com.getpcpanel.device; + +import com.getpcpanel.device.provider.pcpanel.OutputInterpreter; +import com.getpcpanel.device.provider.pcpanel.DeviceScanner; +import com.getpcpanel.device.provider.pcpanel.DeviceCommunicationHandler; import java.util.Collection; import java.util.Map; @@ -17,10 +21,10 @@ import com.getpcpanel.commands.Commands; import com.getpcpanel.commands.command.Command; -import com.getpcpanel.cpp.windows.WindowFocusChangedEvent; -import com.getpcpanel.device.DescriptorFactory; +import com.getpcpanel.profile.WindowFocusChangedEvent; +import com.getpcpanel.device.provider.pcpanel.DescriptorFactory; +import com.getpcpanel.device.provider.pcpanel.PcPanelDeviceFactory; import com.getpcpanel.device.Device; -import com.getpcpanel.device.DeviceFactory; import com.getpcpanel.device.descriptor.DeviceDescriptor; import com.getpcpanel.profile.DeviceSave; import com.getpcpanel.profile.SaveService; @@ -33,7 +37,8 @@ public class DeviceHolder { private final Map devices = new ConcurrentHashMap<>(); @Inject SaveService saveService; - @Inject DeviceFactory deviceFactory; + @Inject PcPanelDeviceFactory pcPanelDeviceFactory; + @Inject GenericDeviceFactory genericDeviceFactory; @Inject OutputInterpreter outputInterpreter; @Inject Event eventBus; @@ -68,8 +73,8 @@ public void deviceAdded(@Observes DeviceScanner.DeviceConnectedEvent event) { // steps (init packet + push lighting) run only for a lighting-capable PCPanel/HID device; a // lightless device (Deej) has no output channel and skips both. var device = isPcPanel - ? deviceFactory.build(event.serialNum(), deviceSave, descriptor) - : deviceFactory.buildGeneric(event.serialNum(), deviceSave, descriptor); + ? pcPanelDeviceFactory.build(event.serialNum(), deviceSave, descriptor) + : genericDeviceFactory.build(event.serialNum(), deviceSave, descriptor); // A descriptor-only device (MIDI/Deej) grows its descriptor as new controls are learned, which // re-fires DeviceConnectedEvent and rebuilds the device here with a zeroed rotation array. Carry // the previously-learned knob positions forward so prior controls don't snap back to 0. diff --git a/src/main/java/com/getpcpanel/device/GenericDevice.java b/src/main/java/com/getpcpanel/device/GenericDevice.java index 53d37e2d..6b7f4624 100644 --- a/src/main/java/com/getpcpanel/device/GenericDevice.java +++ b/src/main/java/com/getpcpanel/device/GenericDevice.java @@ -1,9 +1,11 @@ package com.getpcpanel.device; +import com.getpcpanel.device.provider.pcpanel.DeviceType; + import com.getpcpanel.commands.IconService; import com.getpcpanel.device.descriptor.AnalogInputSpec; import com.getpcpanel.device.descriptor.DeviceDescriptor; -import com.getpcpanel.hid.OutputInterpreter; +import com.getpcpanel.device.provider.pcpanel.OutputInterpreter; import com.getpcpanel.profile.DeviceSave; import com.getpcpanel.profile.SaveService; diff --git a/src/main/java/com/getpcpanel/device/GenericDeviceFactory.java b/src/main/java/com/getpcpanel/device/GenericDeviceFactory.java new file mode 100644 index 00000000..86bddf8b --- /dev/null +++ b/src/main/java/com/getpcpanel/device/GenericDeviceFactory.java @@ -0,0 +1,30 @@ +package com.getpcpanel.device; + +import com.getpcpanel.device.provider.pcpanel.DeviceType; + +import com.getpcpanel.commands.IconService; +import com.getpcpanel.device.descriptor.DeviceDescriptor; +import com.getpcpanel.device.provider.pcpanel.OutputInterpreter; +import com.getpcpanel.profile.DeviceSave; +import com.getpcpanel.profile.SaveService; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; + +/** + * Builds a descriptor-only {@link GenericDevice} (no {@link DeviceType}, no buttons, no lights) for any + * non-PCPanel provider such as Deej or MIDI. PCPanel devices are built by + * {@link com.getpcpanel.device.provider.pcpanel.PcPanelDeviceFactory} instead. + */ +@ApplicationScoped +public class GenericDeviceFactory { + @Inject SaveService saveService; + @Inject OutputInterpreter outputInterpreter; + @Inject IconService iconService; + @Inject Event eventBus; + + public Device build(String serialNum, DeviceSave deviceSave, DeviceDescriptor descriptor) { + return new GenericDevice(saveService, outputInterpreter, iconService, eventBus, serialNum, deviceSave, descriptor); + } +} diff --git a/src/main/java/com/getpcpanel/device/provider/deej/DeejSerialProvider.java b/src/main/java/com/getpcpanel/device/provider/deej/DeejSerialProvider.java index b1c5043b..e456acee 100644 --- a/src/main/java/com/getpcpanel/device/provider/deej/DeejSerialProvider.java +++ b/src/main/java/com/getpcpanel/device/provider/deej/DeejSerialProvider.java @@ -11,12 +11,11 @@ import com.getpcpanel.device.descriptor.AnalogKind; import com.getpcpanel.device.descriptor.DeviceDescriptor; import com.getpcpanel.device.descriptor.DiscoveryMode; -import com.getpcpanel.device.io.SerialTransport; -import com.getpcpanel.device.io.SerialTransport.SerialConnection; +import com.getpcpanel.device.provider.deej.SerialTransport.SerialConnection; import com.getpcpanel.device.provider.DeviceProvider; import com.getpcpanel.device.provider.deej.DeejProtocol.NoiseReduction; -import com.getpcpanel.hid.DeviceCommunicationHandler.KnobRotateEvent; -import com.getpcpanel.hid.DeviceScanner; +import com.getpcpanel.device.provider.pcpanel.DeviceCommunicationHandler.KnobRotateEvent; +import com.getpcpanel.device.provider.pcpanel.DeviceScanner; import com.getpcpanel.profile.DeviceSave; import com.getpcpanel.profile.SaveService; diff --git a/src/main/java/com/getpcpanel/device/io/JSerialCommArchFix.java b/src/main/java/com/getpcpanel/device/provider/deej/JSerialCommArchFix.java similarity index 99% rename from src/main/java/com/getpcpanel/device/io/JSerialCommArchFix.java rename to src/main/java/com/getpcpanel/device/provider/deej/JSerialCommArchFix.java index dbd05900..e7f5665c 100644 --- a/src/main/java/com/getpcpanel/device/io/JSerialCommArchFix.java +++ b/src/main/java/com/getpcpanel/device/provider/deej/JSerialCommArchFix.java @@ -1,4 +1,4 @@ -package com.getpcpanel.device.io; +package com.getpcpanel.device.provider.deej; import java.nio.file.Files; import java.nio.file.Path; diff --git a/src/main/java/com/getpcpanel/device/io/JSerialCommTransport.java b/src/main/java/com/getpcpanel/device/provider/deej/JSerialCommTransport.java similarity index 99% rename from src/main/java/com/getpcpanel/device/io/JSerialCommTransport.java rename to src/main/java/com/getpcpanel/device/provider/deej/JSerialCommTransport.java index 56e7f97a..47334ef6 100644 --- a/src/main/java/com/getpcpanel/device/io/JSerialCommTransport.java +++ b/src/main/java/com/getpcpanel/device/provider/deej/JSerialCommTransport.java @@ -1,4 +1,4 @@ -package com.getpcpanel.device.io; +package com.getpcpanel.device.provider.deej; import java.util.List; import java.util.function.Consumer; diff --git a/src/main/java/com/getpcpanel/device/io/SerialTransport.java b/src/main/java/com/getpcpanel/device/provider/deej/SerialTransport.java similarity index 97% rename from src/main/java/com/getpcpanel/device/io/SerialTransport.java rename to src/main/java/com/getpcpanel/device/provider/deej/SerialTransport.java index 41619ac4..a8052494 100644 --- a/src/main/java/com/getpcpanel/device/io/SerialTransport.java +++ b/src/main/java/com/getpcpanel/device/provider/deej/SerialTransport.java @@ -1,4 +1,4 @@ -package com.getpcpanel.device.io; +package com.getpcpanel.device.provider.deej; import java.util.List; import java.util.function.Consumer; diff --git a/src/main/java/com/getpcpanel/device/io/JavaxMidiTransport.java b/src/main/java/com/getpcpanel/device/provider/midi/JavaxMidiTransport.java similarity index 92% rename from src/main/java/com/getpcpanel/device/io/JavaxMidiTransport.java rename to src/main/java/com/getpcpanel/device/provider/midi/JavaxMidiTransport.java index cae09e41..cabff914 100644 --- a/src/main/java/com/getpcpanel/device/io/JavaxMidiTransport.java +++ b/src/main/java/com/getpcpanel/device/provider/midi/JavaxMidiTransport.java @@ -1,4 +1,4 @@ -package com.getpcpanel.device.io; +package com.getpcpanel.device.provider.midi; import java.util.ArrayList; import java.util.List; @@ -55,7 +55,7 @@ public List listInputs() { } @Override - public MidiConnection open(String id, Consumer onMessage, Consumer onError) { + public MidiConnection open(String id, Consumer onMessage, Consumer onError) { try { for (var info : MidiSystem.getMidiDeviceInfo()) { if (!idFor(info).equals(id)) { @@ -86,9 +86,9 @@ private static String idFor(javax.sound.midi.MidiDevice.Info info) { /** Decodes each inbound {@link ShortMessage} to the raw triple; everything else is dropped. */ private static final class DecodingReceiver implements Receiver { - private final Consumer onMessage; + private final Consumer onMessage; - private DecodingReceiver(Consumer onMessage) { + private DecodingReceiver(Consumer onMessage) { this.onMessage = onMessage; } @@ -97,7 +97,7 @@ public void send(javax.sound.midi.MidiMessage message, long timeStamp) { // One bad message must never kill the receiver thread (mirror the HID reader guard). try { if (message instanceof ShortMessage sm) { - onMessage.accept(new com.getpcpanel.device.io.MidiTransport.MidiMessage(sm.getStatus(), sm.getData1(), sm.getData2())); + onMessage.accept(new com.getpcpanel.device.provider.midi.MidiTransport.MidiMessage(sm.getStatus(), sm.getData1(), sm.getData2())); } } catch (Throwable t) { // Swallow: decoding/handling is the consumer's job and it guards itself too. diff --git a/src/main/java/com/getpcpanel/device/provider/midi/MidiProvider.java b/src/main/java/com/getpcpanel/device/provider/midi/MidiProvider.java index dc281776..08eac668 100644 --- a/src/main/java/com/getpcpanel/device/provider/midi/MidiProvider.java +++ b/src/main/java/com/getpcpanel/device/provider/midi/MidiProvider.java @@ -15,16 +15,15 @@ import com.getpcpanel.device.descriptor.DeviceDescriptor; import com.getpcpanel.device.descriptor.DigitalInputSpec; import com.getpcpanel.device.descriptor.DiscoveryMode; -import com.getpcpanel.device.io.MidiTransport; -import com.getpcpanel.device.io.MidiTransport.MidiConnection; -import com.getpcpanel.device.io.MidiTransport.MidiDeviceInfo; -import com.getpcpanel.device.io.MidiTransport.MidiMessage; +import com.getpcpanel.device.provider.midi.MidiTransport.MidiConnection; +import com.getpcpanel.device.provider.midi.MidiTransport.MidiDeviceInfo; +import com.getpcpanel.device.provider.midi.MidiTransport.MidiMessage; import com.getpcpanel.device.provider.DeviceProvider; import com.getpcpanel.device.provider.midi.MidiProtocol.MidiEvent; import com.getpcpanel.device.provider.midi.MidiProtocol.MidiKind; -import com.getpcpanel.hid.DeviceCommunicationHandler.ButtonPressEvent; -import com.getpcpanel.hid.DeviceCommunicationHandler.KnobRotateEvent; -import com.getpcpanel.hid.DeviceScanner; +import com.getpcpanel.device.provider.pcpanel.DeviceCommunicationHandler.ButtonPressEvent; +import com.getpcpanel.device.provider.pcpanel.DeviceCommunicationHandler.KnobRotateEvent; +import com.getpcpanel.device.provider.pcpanel.DeviceScanner; import com.getpcpanel.profile.DeviceSave; import com.getpcpanel.profile.SaveService; diff --git a/src/main/java/com/getpcpanel/device/io/MidiTransport.java b/src/main/java/com/getpcpanel/device/provider/midi/MidiTransport.java similarity index 98% rename from src/main/java/com/getpcpanel/device/io/MidiTransport.java rename to src/main/java/com/getpcpanel/device/provider/midi/MidiTransport.java index ae781380..b56d94f5 100644 --- a/src/main/java/com/getpcpanel/device/io/MidiTransport.java +++ b/src/main/java/com/getpcpanel/device/provider/midi/MidiTransport.java @@ -1,4 +1,4 @@ -package com.getpcpanel.device.io; +package com.getpcpanel.device.provider.midi; import java.util.List; import java.util.function.Consumer; diff --git a/src/main/java/com/getpcpanel/hid/ButtonClickEvent.java b/src/main/java/com/getpcpanel/device/provider/pcpanel/ButtonClickEvent.java similarity index 63% rename from src/main/java/com/getpcpanel/hid/ButtonClickEvent.java rename to src/main/java/com/getpcpanel/device/provider/pcpanel/ButtonClickEvent.java index a7a99af9..0b34dadd 100644 --- a/src/main/java/com/getpcpanel/hid/ButtonClickEvent.java +++ b/src/main/java/com/getpcpanel/device/provider/pcpanel/ButtonClickEvent.java @@ -1,4 +1,4 @@ -package com.getpcpanel.hid; +package com.getpcpanel.device.provider.pcpanel; public record ButtonClickEvent(String serialNum, int button, boolean dblClick) { } diff --git a/src/main/java/com/getpcpanel/hid/ByteWriter.java b/src/main/java/com/getpcpanel/device/provider/pcpanel/ByteWriter.java similarity index 98% rename from src/main/java/com/getpcpanel/hid/ByteWriter.java rename to src/main/java/com/getpcpanel/device/provider/pcpanel/ByteWriter.java index 0b8db8f3..7b649b35 100644 --- a/src/main/java/com/getpcpanel/hid/ByteWriter.java +++ b/src/main/java/com/getpcpanel/device/provider/pcpanel/ByteWriter.java @@ -1,4 +1,4 @@ -package com.getpcpanel.hid; +package com.getpcpanel.device.provider.pcpanel; import java.util.stream.Stream; diff --git a/src/main/java/com/getpcpanel/device/DescriptorFactory.java b/src/main/java/com/getpcpanel/device/provider/pcpanel/DescriptorFactory.java similarity index 98% rename from src/main/java/com/getpcpanel/device/DescriptorFactory.java rename to src/main/java/com/getpcpanel/device/provider/pcpanel/DescriptorFactory.java index 6419a9e8..a2c70e0c 100644 --- a/src/main/java/com/getpcpanel/device/DescriptorFactory.java +++ b/src/main/java/com/getpcpanel/device/provider/pcpanel/DescriptorFactory.java @@ -1,4 +1,6 @@ -package com.getpcpanel.device; +package com.getpcpanel.device.provider.pcpanel; + +import com.getpcpanel.device.provider.pcpanel.DeviceType; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/com/getpcpanel/hid/DeviceCommunicationHandler.java b/src/main/java/com/getpcpanel/device/provider/pcpanel/DeviceCommunicationHandler.java similarity index 99% rename from src/main/java/com/getpcpanel/hid/DeviceCommunicationHandler.java rename to src/main/java/com/getpcpanel/device/provider/pcpanel/DeviceCommunicationHandler.java index 4a223089..d5442921 100644 --- a/src/main/java/com/getpcpanel/hid/DeviceCommunicationHandler.java +++ b/src/main/java/com/getpcpanel/device/provider/pcpanel/DeviceCommunicationHandler.java @@ -1,4 +1,4 @@ -package com.getpcpanel.hid; +package com.getpcpanel.device.provider.pcpanel; import java.util.ArrayDeque; import java.util.Arrays; diff --git a/src/main/java/com/getpcpanel/hid/DeviceCommunicationHandlerFactory.java b/src/main/java/com/getpcpanel/device/provider/pcpanel/DeviceCommunicationHandlerFactory.java similarity index 93% rename from src/main/java/com/getpcpanel/hid/DeviceCommunicationHandlerFactory.java rename to src/main/java/com/getpcpanel/device/provider/pcpanel/DeviceCommunicationHandlerFactory.java index a0e84aa0..70969a0b 100644 --- a/src/main/java/com/getpcpanel/hid/DeviceCommunicationHandlerFactory.java +++ b/src/main/java/com/getpcpanel/device/provider/pcpanel/DeviceCommunicationHandlerFactory.java @@ -1,4 +1,4 @@ -package com.getpcpanel.hid; +package com.getpcpanel.device.provider.pcpanel; import org.hid4java.HidDevice; import jakarta.enterprise.event.Event; diff --git a/src/main/java/com/getpcpanel/hid/DeviceScanner.java b/src/main/java/com/getpcpanel/device/provider/pcpanel/DeviceScanner.java similarity index 98% rename from src/main/java/com/getpcpanel/hid/DeviceScanner.java rename to src/main/java/com/getpcpanel/device/provider/pcpanel/DeviceScanner.java index e061e6e1..d746d2d9 100644 --- a/src/main/java/com/getpcpanel/hid/DeviceScanner.java +++ b/src/main/java/com/getpcpanel/device/provider/pcpanel/DeviceScanner.java @@ -1,4 +1,4 @@ -package com.getpcpanel.hid; +package com.getpcpanel.device.provider.pcpanel; import java.util.Optional; import java.util.ArrayList; @@ -15,12 +15,12 @@ import org.hid4java.ScanMode; import org.hid4java.event.HidServicesEvent; -import com.getpcpanel.device.DescriptorFactory; -import com.getpcpanel.device.DeviceType; +import com.getpcpanel.device.provider.pcpanel.DescriptorFactory; +import com.getpcpanel.device.provider.pcpanel.DeviceType; import com.getpcpanel.device.descriptor.DeviceDescriptor; import com.getpcpanel.device.descriptor.DiscoveryMode; import com.getpcpanel.device.provider.DeviceProvider; -import com.getpcpanel.util.OsxPermissionHelper; +import com.getpcpanel.util.os.OsxPermissionHelper; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Event; diff --git a/src/main/java/com/getpcpanel/device/DeviceType.java b/src/main/java/com/getpcpanel/device/provider/pcpanel/DeviceType.java similarity index 93% rename from src/main/java/com/getpcpanel/device/DeviceType.java rename to src/main/java/com/getpcpanel/device/provider/pcpanel/DeviceType.java index 098e6d08..ccfa6e38 100644 --- a/src/main/java/com/getpcpanel/device/DeviceType.java +++ b/src/main/java/com/getpcpanel/device/provider/pcpanel/DeviceType.java @@ -1,4 +1,4 @@ -package com.getpcpanel.device; +package com.getpcpanel.device.provider.pcpanel; import java.util.Arrays; import java.util.List; diff --git a/src/main/java/com/getpcpanel/hid/HidDebug.java b/src/main/java/com/getpcpanel/device/provider/pcpanel/HidDebug.java similarity index 95% rename from src/main/java/com/getpcpanel/hid/HidDebug.java rename to src/main/java/com/getpcpanel/device/provider/pcpanel/HidDebug.java index e0e349bb..b4ccf788 100644 --- a/src/main/java/com/getpcpanel/hid/HidDebug.java +++ b/src/main/java/com/getpcpanel/device/provider/pcpanel/HidDebug.java @@ -1,4 +1,4 @@ -package com.getpcpanel.hid; +package com.getpcpanel.device.provider.pcpanel; import java.io.File; import java.io.FileOutputStream; @@ -8,7 +8,7 @@ import org.hid4java.HidServicesListener; import org.hid4java.event.HidServicesEvent; -import com.getpcpanel.util.PcPanelRoot; +import com.getpcpanel.util.io.PcPanelRoot; import lombok.SneakyThrows; diff --git a/src/main/java/com/getpcpanel/hid/InputInterpreter.java b/src/main/java/com/getpcpanel/device/provider/pcpanel/InputInterpreter.java similarity index 96% rename from src/main/java/com/getpcpanel/hid/InputInterpreter.java rename to src/main/java/com/getpcpanel/device/provider/pcpanel/InputInterpreter.java index 2eae2a93..bafa7555 100644 --- a/src/main/java/com/getpcpanel/hid/InputInterpreter.java +++ b/src/main/java/com/getpcpanel/device/provider/pcpanel/InputInterpreter.java @@ -1,4 +1,7 @@ -package com.getpcpanel.hid; +package com.getpcpanel.device.provider.pcpanel; + +import com.getpcpanel.device.DeviceHolder; +import com.getpcpanel.commands.DialValue; import static com.getpcpanel.commands.Commands.hasCommands; import static java.util.Objects.requireNonNullElse; @@ -17,7 +20,7 @@ import com.getpcpanel.commands.PCPanelControlEvent; import com.getpcpanel.profile.BaseLayerService; import com.getpcpanel.profile.SaveService; -import com.getpcpanel.util.Debouncer; +import com.getpcpanel.util.concurrent.Debouncer; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; diff --git a/src/main/java/com/getpcpanel/hid/OutputInterpreter.java b/src/main/java/com/getpcpanel/device/provider/pcpanel/OutputInterpreter.java similarity index 99% rename from src/main/java/com/getpcpanel/hid/OutputInterpreter.java rename to src/main/java/com/getpcpanel/device/provider/pcpanel/OutputInterpreter.java index 117dbc51..7c93207b 100644 --- a/src/main/java/com/getpcpanel/hid/OutputInterpreter.java +++ b/src/main/java/com/getpcpanel/device/provider/pcpanel/OutputInterpreter.java @@ -1,8 +1,10 @@ -package com.getpcpanel.hid; +package com.getpcpanel.device.provider.pcpanel; + +import com.getpcpanel.integration.device.BrightnessService; import java.util.Arrays; -import com.getpcpanel.device.DeviceType; +import com.getpcpanel.device.provider.pcpanel.DeviceType; import com.getpcpanel.profile.BaseLayerService; import com.getpcpanel.profile.dto.LightingConfig; import com.getpcpanel.profile.dto.SingleKnobLightingConfig; diff --git a/src/main/java/com/getpcpanel/device/PCPanelMiniDevice.java b/src/main/java/com/getpcpanel/device/provider/pcpanel/PCPanelMiniDevice.java similarity index 89% rename from src/main/java/com/getpcpanel/device/PCPanelMiniDevice.java rename to src/main/java/com/getpcpanel/device/provider/pcpanel/PCPanelMiniDevice.java index 7faba3d6..7946d354 100644 --- a/src/main/java/com/getpcpanel/device/PCPanelMiniDevice.java +++ b/src/main/java/com/getpcpanel/device/provider/pcpanel/PCPanelMiniDevice.java @@ -1,9 +1,10 @@ -package com.getpcpanel.device; +package com.getpcpanel.device.provider.pcpanel; + +import com.getpcpanel.device.Device; +import com.getpcpanel.device.provider.pcpanel.DeviceType; import com.getpcpanel.commands.IconService; import com.getpcpanel.device.descriptor.DeviceDescriptor; -import com.getpcpanel.hid.InputInterpreter; -import com.getpcpanel.hid.OutputInterpreter; import com.getpcpanel.profile.DeviceSave; import com.getpcpanel.profile.SaveService; import com.getpcpanel.util.coloroverride.OverrideColorService; diff --git a/src/main/java/com/getpcpanel/device/PCPanelProDevice.java b/src/main/java/com/getpcpanel/device/provider/pcpanel/PCPanelProDevice.java similarity index 89% rename from src/main/java/com/getpcpanel/device/PCPanelProDevice.java rename to src/main/java/com/getpcpanel/device/provider/pcpanel/PCPanelProDevice.java index 3bc62fd1..eb722fb5 100644 --- a/src/main/java/com/getpcpanel/device/PCPanelProDevice.java +++ b/src/main/java/com/getpcpanel/device/provider/pcpanel/PCPanelProDevice.java @@ -1,9 +1,10 @@ -package com.getpcpanel.device; +package com.getpcpanel.device.provider.pcpanel; + +import com.getpcpanel.device.Device; +import com.getpcpanel.device.provider.pcpanel.DeviceType; import com.getpcpanel.commands.IconService; import com.getpcpanel.device.descriptor.DeviceDescriptor; -import com.getpcpanel.hid.InputInterpreter; -import com.getpcpanel.hid.OutputInterpreter; import com.getpcpanel.profile.DeviceSave; import com.getpcpanel.profile.SaveService; import com.getpcpanel.util.coloroverride.OverrideColorService; diff --git a/src/main/java/com/getpcpanel/device/PCPanelRGBDevice.java b/src/main/java/com/getpcpanel/device/provider/pcpanel/PCPanelRGBDevice.java similarity index 89% rename from src/main/java/com/getpcpanel/device/PCPanelRGBDevice.java rename to src/main/java/com/getpcpanel/device/provider/pcpanel/PCPanelRGBDevice.java index f26fe322..de609166 100644 --- a/src/main/java/com/getpcpanel/device/PCPanelRGBDevice.java +++ b/src/main/java/com/getpcpanel/device/provider/pcpanel/PCPanelRGBDevice.java @@ -1,9 +1,10 @@ -package com.getpcpanel.device; +package com.getpcpanel.device.provider.pcpanel; + +import com.getpcpanel.device.Device; +import com.getpcpanel.device.provider.pcpanel.DeviceType; import com.getpcpanel.commands.IconService; import com.getpcpanel.device.descriptor.DeviceDescriptor; -import com.getpcpanel.hid.InputInterpreter; -import com.getpcpanel.hid.OutputInterpreter; import com.getpcpanel.profile.DeviceSave; import com.getpcpanel.profile.SaveService; import com.getpcpanel.util.coloroverride.OverrideColorService; diff --git a/src/main/java/com/getpcpanel/device/DeviceFactory.java b/src/main/java/com/getpcpanel/device/provider/pcpanel/PcPanelDeviceFactory.java similarity index 62% rename from src/main/java/com/getpcpanel/device/DeviceFactory.java rename to src/main/java/com/getpcpanel/device/provider/pcpanel/PcPanelDeviceFactory.java index 5284020d..c008c15d 100644 --- a/src/main/java/com/getpcpanel/device/DeviceFactory.java +++ b/src/main/java/com/getpcpanel/device/provider/pcpanel/PcPanelDeviceFactory.java @@ -1,19 +1,26 @@ -package com.getpcpanel.device; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.event.Event; -import jakarta.inject.Inject; +package com.getpcpanel.device.provider.pcpanel; import com.getpcpanel.commands.IconService; +import com.getpcpanel.device.Device; +import com.getpcpanel.device.provider.pcpanel.DeviceType; import com.getpcpanel.device.descriptor.DeviceDescriptor; -import com.getpcpanel.hid.InputInterpreter; -import com.getpcpanel.hid.OutputInterpreter; import com.getpcpanel.profile.DeviceSave; import com.getpcpanel.profile.SaveService; import com.getpcpanel.util.coloroverride.OverrideColorService; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; + +/** + * Builds the concrete PCPanel device model (RGB / Mini / Pro) for a connected PCPanel device. The + * subclass is chosen from the descriptor's device-kind (the PCPanel {@link DeviceType}); the subclasses + * are otherwise identical and exist only for the per-model {@link Device#deviceType()} value and array + * size. This is PCPanel-provider-internal construction — non-PCPanel providers use + * {@link com.getpcpanel.device.GenericDeviceFactory}. + */ @ApplicationScoped -public class DeviceFactory { +public class PcPanelDeviceFactory { @Inject InputInterpreter inputInterpreter; @Inject SaveService saveService; @Inject OutputInterpreter outputInterpreter; @@ -21,11 +28,6 @@ public class DeviceFactory { @Inject OverrideColorService overrideColorService; @Inject Event eventBus; - /** - * Builds a PCPanel device from its capability descriptor. The concrete subclass is chosen from - * the descriptor's device-kind (the PCPanel {@link DeviceType}); the subclasses are otherwise - * identical and exist only for the per-model {@link Device#deviceType()} value and array size. - */ public Device build(String serialNum, DeviceSave deviceSave, DeviceDescriptor descriptor) { var type = DeviceType.valueOf(descriptor.deviceKindId()); return switch (type) { @@ -34,12 +36,4 @@ public Device build(String serialNum, DeviceSave deviceSave, DeviceDescriptor de case PCPANEL_PRO -> new PCPanelProDevice(inputInterpreter, saveService, outputInterpreter, iconService, overrideColorService, eventBus, serialNum, deviceSave, descriptor); }; } - - /** - * Builds a descriptor-only {@link GenericDevice} (no {@link DeviceType}, no buttons, no lights) - * for any non-PCPanel provider such as Deej. - */ - public Device buildGeneric(String serialNum, DeviceSave deviceSave, DeviceDescriptor descriptor) { - return new GenericDevice(saveService, outputInterpreter, iconService, eventBus, serialNum, deviceSave, descriptor); - } } diff --git a/src/main/java/com/getpcpanel/rest/ProVisualColorsService.java b/src/main/java/com/getpcpanel/device/provider/pcpanel/ProVisualColorsService.java similarity index 99% rename from src/main/java/com/getpcpanel/rest/ProVisualColorsService.java rename to src/main/java/com/getpcpanel/device/provider/pcpanel/ProVisualColorsService.java index b040e2d6..f2808bef 100644 --- a/src/main/java/com/getpcpanel/rest/ProVisualColorsService.java +++ b/src/main/java/com/getpcpanel/device/provider/pcpanel/ProVisualColorsService.java @@ -1,11 +1,11 @@ -package com.getpcpanel.rest; +package com.getpcpanel.device.provider.pcpanel; import java.util.ArrayList; import java.util.Collections; import java.util.List; import com.getpcpanel.device.Device; -import com.getpcpanel.device.DeviceType; +import com.getpcpanel.device.provider.pcpanel.DeviceType; import com.getpcpanel.profile.BaseLayerService; import com.getpcpanel.profile.dto.LightingConfig; import com.getpcpanel.profile.dto.SingleKnobLightingConfig; diff --git a/src/main/java/com/getpcpanel/rest/DeviceResource.java b/src/main/java/com/getpcpanel/device/rest/DeviceResource.java similarity index 99% rename from src/main/java/com/getpcpanel/rest/DeviceResource.java rename to src/main/java/com/getpcpanel/device/rest/DeviceResource.java index b0668862..c03acdb6 100644 --- a/src/main/java/com/getpcpanel/rest/DeviceResource.java +++ b/src/main/java/com/getpcpanel/device/rest/DeviceResource.java @@ -1,4 +1,4 @@ -package com.getpcpanel.rest; +package com.getpcpanel.device.rest; import java.util.ArrayList; import java.util.List; @@ -6,7 +6,7 @@ import com.getpcpanel.commands.Commands; import com.getpcpanel.device.Device; -import com.getpcpanel.hid.DeviceHolder; +import com.getpcpanel.device.DeviceHolder; import com.getpcpanel.profile.DeviceSave; import com.getpcpanel.profile.LightingChangedToDefaultEvent; import com.getpcpanel.profile.Profile; diff --git a/src/main/java/com/getpcpanel/rest/MidiResource.java b/src/main/java/com/getpcpanel/device/rest/MidiResource.java similarity index 97% rename from src/main/java/com/getpcpanel/rest/MidiResource.java rename to src/main/java/com/getpcpanel/device/rest/MidiResource.java index cb297f9e..8c128731 100644 --- a/src/main/java/com/getpcpanel/rest/MidiResource.java +++ b/src/main/java/com/getpcpanel/device/rest/MidiResource.java @@ -1,4 +1,4 @@ -package com.getpcpanel.rest; +package com.getpcpanel.device.rest; import java.util.List; diff --git a/src/main/java/com/getpcpanel/rest/SerialResource.java b/src/main/java/com/getpcpanel/device/rest/SerialResource.java similarity index 98% rename from src/main/java/com/getpcpanel/rest/SerialResource.java rename to src/main/java/com/getpcpanel/device/rest/SerialResource.java index 48c44277..af871e27 100644 --- a/src/main/java/com/getpcpanel/rest/SerialResource.java +++ b/src/main/java/com/getpcpanel/device/rest/SerialResource.java @@ -1,4 +1,4 @@ -package com.getpcpanel.rest; +package com.getpcpanel.device.rest; import java.util.List; diff --git a/src/main/java/com/getpcpanel/graalvm/JnaWin32ReflectionConfig.java b/src/main/java/com/getpcpanel/graalvm/JnaWin32ReflectionConfig.java index 6079adbd..15a7ac32 100644 --- a/src/main/java/com/getpcpanel/graalvm/JnaWin32ReflectionConfig.java +++ b/src/main/java/com/getpcpanel/graalvm/JnaWin32ReflectionConfig.java @@ -1,5 +1,10 @@ package com.getpcpanel.graalvm; +import com.getpcpanel.sleepdetection.Win32Desktop; +import com.getpcpanel.sleepdetection.Win32PowerNotify; +import com.getpcpanel.util.tray.win.WinShell32; +import com.getpcpanel.util.tray.win.WinUser32Ext; + import io.quarkus.runtime.annotations.RegisterForReflection; /** @@ -15,7 +20,15 @@ * covered by the generated reachability metadata; this class adds the window-creation and * window-messaging types used by the power/session helper window and the volume overlay. */ -@RegisterForReflection(classNames = { +@RegisterForReflection(targets = { + // Project (PCPanel) JNA types — referenced by .class so a package move is compiler-checked + // rather than silently breaking the native build. + WinShell32.class, + WinShell32.NOTIFYICONDATA.class, + WinUser32Ext.class, + Win32Desktop.class, + Win32PowerNotify.class, +}, classNames = { "com.sun.jna.platform.win32.WinDef$HMODULE", "com.sun.jna.platform.win32.WinDef$HINSTANCE", "com.sun.jna.platform.win32.WinDef$HWND", @@ -37,20 +50,10 @@ "com.sun.jna.platform.win32.WinUser$MSG", "com.sun.jna.platform.win32.WinDef$POINT", "com.sun.jna.platform.win32.WinUser$BLENDFUNCTION", - // System-tray NOTIFYICONDATA struct (and the GUID it embeds): JNA reads the struct's declared - // fields reflectively to compute its layout. Without this, Structure.getFieldOrder() sees no - // fields in the native image and the tray fails with "declared field names ([])". - "com.getpcpanel.util.tray.win.WinShell32$NOTIFYICONDATA", + // The GUID embedded in the tray NOTIFYICONDATA struct (the struct itself is a .class target above). "com.sun.jna.platform.win32.Guid$GUID", - // The tray JNA library interfaces themselves: JNA reads their static INSTANCE field (and maps - // their methods) reflectively, so the interface types need fields+methods registered. - "com.getpcpanel.util.tray.win.WinShell32", - "com.getpcpanel.util.tray.win.WinUser32Ext", - // Desktop-lock detection helper (OpenInputDesktop/CloseDesktop): same reflective JNA mapping. - "com.getpcpanel.sleepdetection.Win32Desktop", - // Display-power detection: the power-notify library and the window-procedure callback whose - // "callback" method JNA invokes reflectively from the native message dispatch. - "com.getpcpanel.sleepdetection.Win32PowerNotify", + // The window-procedure callback whose "callback" method JNA invokes reflectively from the + // native message dispatch (used by Win32PowerNotify, a .class target above). "com.sun.jna.platform.win32.WinUser$WindowProc", // SendInput keyboard synthesis (CommandMedia media keys, WindowsKeyboard keystrokes): JNA // reflectively instantiates the INPUT union, its INPUT_UNION and every member struct to compute diff --git a/src/main/java/com/getpcpanel/graalvm/NativeImageConfig.java b/src/main/java/com/getpcpanel/graalvm/NativeImageConfig.java index 2a7b34e3..ef46da2c 100644 --- a/src/main/java/com/getpcpanel/graalvm/NativeImageConfig.java +++ b/src/main/java/com/getpcpanel/graalvm/NativeImageConfig.java @@ -12,64 +12,68 @@ import com.getpcpanel.commands.Commands; import com.getpcpanel.commands.CommandsType; import com.getpcpanel.commands.DeviceSet; -import com.getpcpanel.commands.command.AnalogBand; +import com.getpcpanel.integration.analogbands.command.AnalogBand; import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandAnalogBands; -import com.getpcpanel.commands.command.CommandBrightness; -import com.getpcpanel.commands.command.CommandEndProgram; -import com.getpcpanel.commands.command.CommandHttpRequest; -import com.getpcpanel.commands.command.CommandKeystroke; -import com.getpcpanel.commands.command.CommandMedia; -import com.getpcpanel.commands.command.CommandMedia.VolumeButton; -import com.getpcpanel.commands.command.CommandMqttPublish; +import com.getpcpanel.integration.analogbands.command.CommandAnalogBands; +import com.getpcpanel.integration.device.command.CommandBrightness; +import com.getpcpanel.integration.program.command.CommandEndProgram; +import com.getpcpanel.integration.output.command.CommandHttpRequest; +import com.getpcpanel.integration.keyboard.command.CommandKeystroke; +import com.getpcpanel.integration.keyboard.command.CommandMedia; +import com.getpcpanel.integration.keyboard.command.CommandMedia.VolumeButton; +import com.getpcpanel.integration.mqtt.MqttDeviceService; +import com.getpcpanel.integration.mqtt.MqttHomeAssistantHelper; +import com.getpcpanel.integration.volume.platform.osx.CoreAudioLib; +import com.getpcpanel.integration.mqtt.command.CommandMqttPublish; import com.getpcpanel.commands.command.CommandNoOp; -import com.getpcpanel.commands.command.CommandObs; -import com.getpcpanel.commands.command.CommandObsAction; -import com.getpcpanel.commands.command.CommandObsAction.ObsActionType; -import com.getpcpanel.commands.command.CommandObsMuteSource; -import com.getpcpanel.commands.command.CommandObsSetScene; -import com.getpcpanel.commands.command.CommandObsSetSourceVolume; -import com.getpcpanel.commands.command.CommandOscSend; -import com.getpcpanel.commands.command.CommandProfile; -import com.getpcpanel.commands.command.CommandRun; -import com.getpcpanel.commands.command.CommandShortcut; +import com.getpcpanel.integration.obs.command.CommandObs; +import com.getpcpanel.integration.obs.command.CommandObsAction; +import com.getpcpanel.integration.obs.command.CommandObsAction.ObsActionType; +import com.getpcpanel.integration.obs.command.CommandObsMuteSource; +import com.getpcpanel.integration.obs.command.CommandObsSetScene; +import com.getpcpanel.integration.obs.command.CommandObsSetSourceVolume; +import com.getpcpanel.integration.osc.command.CommandOscSend; +import com.getpcpanel.integration.profile.command.CommandProfile; +import com.getpcpanel.integration.program.command.CommandRun; +import com.getpcpanel.integration.program.command.CommandShortcut; import com.getpcpanel.commands.command.CommandValueOutput; -import com.getpcpanel.commands.command.CommandVoiceMeeter; -import com.getpcpanel.commands.command.CommandVoiceMeeterAdvanced; -import com.getpcpanel.commands.command.CommandVoiceMeeterAdvancedButton; -import com.getpcpanel.commands.command.CommandVoiceMeeterBasic; -import com.getpcpanel.commands.command.CommandVoiceMeeterBasicButton; -import com.getpcpanel.commands.command.CommandVolume; -import com.getpcpanel.commands.command.CommandVolumeApplicationDeviceToggle; -import com.getpcpanel.commands.command.CommandVolumeDefaultDevice; -import com.getpcpanel.commands.command.CommandVolumeDefaultDeviceAdvanced; -import com.getpcpanel.commands.command.CommandVolumeDefaultDeviceToggle; -import com.getpcpanel.commands.command.CommandVolumeDefaultDeviceToggleAdvanced; -import com.getpcpanel.commands.command.CommandVolumeDevice; -import com.getpcpanel.commands.command.CommandVolumeDeviceMute; -import com.getpcpanel.commands.command.CommandVolumeFocus; -import com.getpcpanel.commands.command.CommandVolumeFocusMute; -import com.getpcpanel.commands.command.CommandVolumeProcess; -import com.getpcpanel.commands.command.CommandVolumeProcessMute; +import com.getpcpanel.util.version.Version; +import com.getpcpanel.integration.voicemeeter.command.CommandVoiceMeeter; +import com.getpcpanel.integration.voicemeeter.command.CommandVoiceMeeterAdvanced; +import com.getpcpanel.integration.voicemeeter.command.CommandVoiceMeeterAdvancedButton; +import com.getpcpanel.integration.voicemeeter.command.CommandVoiceMeeterBasic; +import com.getpcpanel.integration.voicemeeter.command.CommandVoiceMeeterBasicButton; +import com.getpcpanel.integration.volume.command.CommandVolume; +import com.getpcpanel.integration.volume.command.CommandVolumeApplicationDeviceToggle; +import com.getpcpanel.integration.volume.command.CommandVolumeDefaultDevice; +import com.getpcpanel.integration.volume.command.CommandVolumeDefaultDeviceAdvanced; +import com.getpcpanel.integration.volume.command.CommandVolumeDefaultDeviceToggle; +import com.getpcpanel.integration.volume.command.CommandVolumeDefaultDeviceToggleAdvanced; +import com.getpcpanel.integration.volume.command.CommandVolumeDevice; +import com.getpcpanel.integration.volume.command.CommandVolumeDeviceMute; +import com.getpcpanel.integration.volume.command.CommandVolumeFocus; +import com.getpcpanel.integration.volume.command.CommandVolumeFocusMute; +import com.getpcpanel.integration.volume.command.CommandVolumeProcess; +import com.getpcpanel.integration.volume.command.CommandVolumeProcessMute; import com.getpcpanel.commands.command.DialAction.DialCommandParams; -import com.getpcpanel.discord.command.CommandDiscord; -import com.getpcpanel.discord.command.CommandDiscordJoinVoice; -import com.getpcpanel.discord.command.CommandDiscordLeaveVoice; -import com.getpcpanel.discord.command.CommandDiscordMute; -import com.getpcpanel.discord.command.CommandDiscordScreenShare; -import com.getpcpanel.discord.command.CommandDiscordToggleVideo; -import com.getpcpanel.discord.command.CommandDiscordVolume; -import com.getpcpanel.discord.command.CommandDiscordSelfDeafen; -import com.getpcpanel.discord.command.CommandDiscordSelfInputVolume; -import com.getpcpanel.discord.command.CommandDiscordSelfMute; -import com.getpcpanel.discord.command.CommandDiscordSelfOutputVolume; -import com.getpcpanel.discord.command.CommandDiscordUserMute; -import com.getpcpanel.discord.command.CommandDiscordUserVolume; -import com.getpcpanel.homeassistant.command.CommandHomeAssistant; -import com.getpcpanel.homeassistant.command.CommandHomeAssistantAction; -import com.getpcpanel.homeassistant.command.CommandHomeAssistantValue; -import com.getpcpanel.homeassistant.dto.HomeAssistantServer; -import com.getpcpanel.homeassistant.dto.HomeAssistantServerStatus; +import com.getpcpanel.integration.discord.command.CommandDiscord; +import com.getpcpanel.integration.discord.command.CommandDiscordJoinVoice; +import com.getpcpanel.integration.discord.command.CommandDiscordLeaveVoice; +import com.getpcpanel.integration.discord.command.CommandDiscordMute; +import com.getpcpanel.integration.discord.command.CommandDiscordScreenShare; +import com.getpcpanel.integration.discord.command.CommandDiscordToggleVideo; +import com.getpcpanel.integration.discord.command.CommandDiscordVolume; +import com.getpcpanel.integration.discord.command.CommandDiscordSelfDeafen; +import com.getpcpanel.integration.discord.command.CommandDiscordSelfInputVolume; +import com.getpcpanel.integration.discord.command.CommandDiscordSelfMute; +import com.getpcpanel.integration.discord.command.CommandDiscordSelfOutputVolume; +import com.getpcpanel.integration.discord.command.CommandDiscordUserMute; +import com.getpcpanel.integration.discord.command.CommandDiscordUserVolume; +import com.getpcpanel.integration.homeassistant.command.CommandHomeAssistant; +import com.getpcpanel.integration.homeassistant.command.CommandHomeAssistantAction; +import com.getpcpanel.integration.homeassistant.command.CommandHomeAssistantValue; +import com.getpcpanel.integration.homeassistant.dto.HomeAssistantServer; +import com.getpcpanel.integration.homeassistant.dto.HomeAssistantServerStatus; import com.getpcpanel.device.descriptor.AnalogInputSpec; import com.getpcpanel.device.descriptor.AnalogKind; import com.getpcpanel.device.descriptor.AnalogOutputSpec; @@ -83,19 +87,19 @@ import com.getpcpanel.profile.DeviceSave; import com.getpcpanel.profile.Profile; import com.getpcpanel.profile.Save; -import com.getpcpanel.profile.dto.DiscordAuth; -import com.getpcpanel.profile.dto.DiscordSeenUser; -import com.getpcpanel.profile.dto.DiscordSettings; -import com.getpcpanel.profile.dto.FocusVolumeOverride; -import com.getpcpanel.profile.dto.FocusVolumeTarget; +import com.getpcpanel.integration.discord.dto.DiscordAuth; +import com.getpcpanel.integration.discord.dto.DiscordSeenUser; +import com.getpcpanel.integration.discord.dto.DiscordSettings; +import com.getpcpanel.integration.volume.FocusVolumeOverride; +import com.getpcpanel.integration.volume.FocusVolumeTarget; import com.getpcpanel.profile.dto.KnobSetting; import com.getpcpanel.profile.dto.LightingConfig; import com.getpcpanel.profile.dto.LightingConfig.LightingMode; -import com.getpcpanel.profile.dto.MqttSettings; -import com.getpcpanel.profile.dto.MqttSettings.HomeAssistantSettings; -import com.getpcpanel.profile.dto.OSCBinding; -import com.getpcpanel.profile.dto.OSCConnectionInfo; -import com.getpcpanel.profile.dto.OverlayPosition; +import com.getpcpanel.integration.mqtt.dto.MqttSettings; +import com.getpcpanel.integration.mqtt.dto.MqttSettings.HomeAssistantSettings; +import com.getpcpanel.integration.osc.dto.OSCBinding; +import com.getpcpanel.integration.osc.dto.OSCConnectionInfo; +import com.getpcpanel.integration.volume.overlay.OverlayPosition; import com.getpcpanel.profile.dto.SingleKnobLightingConfig; import com.getpcpanel.profile.dto.SingleKnobLightingConfig.SINGLE_KNOB_MODE; import com.getpcpanel.profile.dto.SingleLogoLightingConfig; @@ -104,19 +108,19 @@ import com.getpcpanel.profile.dto.SingleSliderLabelLightingConfig.SINGLE_SLIDER_LABEL_MODE; import com.getpcpanel.profile.dto.SingleSliderLightingConfig; import com.getpcpanel.profile.dto.SingleSliderLightingConfig.SINGLE_SLIDER_MODE; -import com.getpcpanel.profile.dto.WaveLinkSettings; +import com.getpcpanel.integration.wavelink.dto.WaveLinkSettings; import com.getpcpanel.rest.model.dto.AddDeejDeviceDto; import com.getpcpanel.rest.model.dto.MidiDeviceDto; import com.getpcpanel.rest.model.dto.OnboardingDto; import com.getpcpanel.rest.model.dto.SerialPortDto; -import com.getpcpanel.wavelink.command.CommandWaveLink; -import com.getpcpanel.wavelink.command.CommandWaveLinkAddFocusToChannel; -import com.getpcpanel.wavelink.command.CommandWaveLinkChange; -import com.getpcpanel.wavelink.command.CommandWaveLinkChangeLevel; -import com.getpcpanel.wavelink.command.CommandWaveLinkChangeMute; -import com.getpcpanel.wavelink.command.CommandWaveLinkChannelEffect; -import com.getpcpanel.wavelink.command.CommandWaveLinkMainOutput; -import com.getpcpanel.wavelink.command.WaveLinkCommandTarget; +import com.getpcpanel.integration.wavelink.command.CommandWaveLink; +import com.getpcpanel.integration.wavelink.command.CommandWaveLinkAddFocusToChannel; +import com.getpcpanel.integration.wavelink.command.CommandWaveLinkChange; +import com.getpcpanel.integration.wavelink.command.CommandWaveLinkChangeLevel; +import com.getpcpanel.integration.wavelink.command.CommandWaveLinkChangeMute; +import com.getpcpanel.integration.wavelink.command.CommandWaveLinkChannelEffect; +import com.getpcpanel.integration.wavelink.command.CommandWaveLinkMainOutput; +import com.getpcpanel.integration.wavelink.command.WaveLinkCommandTarget; import dev.niels.wavelink.impl.model.WaveLinkApp; import dev.niels.wavelink.impl.model.WaveLinkChannel; @@ -177,8 +181,25 @@ LibusbHidApiLibrary.class, WideStringBuffer.class, - // MQTT Home Assistant discovery payload classes (serialised to JSON by Jackson) - // Note: these records are package-private so referenced by classNames below + // MQTT button-click event + Home Assistant discovery payloads (Jackson-serialised). Public so + // they are referenced here by .class — compiler-checked — instead of fragile String class names. + MqttDeviceService.MqttEvent.class, + MqttHomeAssistantHelper.HomeAssistantAvailability.class, + MqttHomeAssistantHelper.HomeAssistantButtonConfig.class, + MqttHomeAssistantHelper.HomeAssistantButtonEventConfig.class, + MqttHomeAssistantHelper.HomeAssistantDevice.class, + MqttHomeAssistantHelper.HomeAssistantLightConfig.class, + MqttHomeAssistantHelper.HomeAssistantNumberConfig.class, + + // GitHub release version model – deserialised by a plain ObjectMapper in VersionChecker + // (records need their canonical creator registered). + Version.class, + Version.SemVer.class, + + // Project CoreAudio JNA binding: the property-address Structure (instantiated per call) and the + // change-listener Callback (used for default-device/volume notifications). + CoreAudioLib.AudioObjectPropertyAddress.class, + CoreAudioLib.AudioObjectPropertyListenerProc.class, // Command type hierarchy Command.class, @@ -386,23 +407,6 @@ "org.eclipse.paho.mqttv5.client.websocket.WebSocketNetworkModuleFactory", "org.eclipse.paho.mqttv5.client.websocket.WebSocketSecureNetworkModuleFactory", - // GitHub release version model – deserialised by a plain ObjectMapper in VersionChecker, - // so Quarkus does not auto-detect it for reflection (records need their canonical creator). - "com.getpcpanel.util.version.Version", - "com.getpcpanel.util.version.Version$SemVer", - - // MQTT button-click event payload (package-private record); Jackson reads its accessor - // reflectively to serialise it on each button press while MQTT is connected. - "com.getpcpanel.mqtt.MqttDeviceService$MqttEvent", - - // MQTT Home Assistant discovery records (package-private inner classes – referenced by name) - "com.getpcpanel.mqtt.MqttHomeAssistantHelper$HomeAssistantAvailability", - "com.getpcpanel.mqtt.MqttHomeAssistantHelper$HomeAssistantButtonConfig", - "com.getpcpanel.mqtt.MqttHomeAssistantHelper$HomeAssistantButtonEventConfig", - "com.getpcpanel.mqtt.MqttHomeAssistantHelper$HomeAssistantDevice", - "com.getpcpanel.mqtt.MqttHomeAssistantHelper$HomeAssistantLightConfig", - "com.getpcpanel.mqtt.MqttHomeAssistantHelper$HomeAssistantNumberConfig", - // macOS CoreAudio path (GET /api/audio/*, default-device switching). JNA reflectively // instantiates these Structures (no-arg constructor + field access). The CoreFoundation class // initializer itself builds a CFTypeID, so the whole jna-platform CoreFoundation inner-class @@ -425,11 +429,6 @@ "com.sun.jna.platform.mac.CoreFoundation$CFStringRef$ByReference", "com.sun.jna.platform.mac.CoreFoundation$CFTypeID", "com.sun.jna.platform.mac.CoreFoundation$CFTypeRef", - // Project CoreAudio JNA binding: the property-address Structure (instantiated per call) and the - // change-listener Callback (used for default-device/volume notifications). - "com.getpcpanel.cpp.osx.CoreAudioLib$AudioObjectPropertyAddress", - "com.getpcpanel.cpp.osx.CoreAudioLib$AudioObjectPropertyListenerProc", - // JNA by-reference pointer types that appear in project Library method signatures. JNA // reflectively instantiates these via their public no-arg constructor when marshalling the // call, so each must be registered or the call throws IllegalArgumentException / diff --git a/src/main/java/com/getpcpanel/analogbands/AnalogBandColorService.java b/src/main/java/com/getpcpanel/integration/analogbands/AnalogBandColorService.java similarity index 98% rename from src/main/java/com/getpcpanel/analogbands/AnalogBandColorService.java rename to src/main/java/com/getpcpanel/integration/analogbands/AnalogBandColorService.java index dc680ad0..bc9856b3 100644 --- a/src/main/java/com/getpcpanel/analogbands/AnalogBandColorService.java +++ b/src/main/java/com/getpcpanel/integration/analogbands/AnalogBandColorService.java @@ -1,4 +1,4 @@ -package com.getpcpanel.analogbands; +package com.getpcpanel.integration.analogbands; import java.util.Map; import java.util.Objects; @@ -6,9 +6,9 @@ import org.apache.commons.lang3.StringUtils; import com.getpcpanel.commands.Commands; -import com.getpcpanel.commands.command.CommandAnalogBands; +import com.getpcpanel.integration.analogbands.command.CommandAnalogBands; import com.getpcpanel.device.Device; -import com.getpcpanel.hid.DeviceHolder; +import com.getpcpanel.device.DeviceHolder; import com.getpcpanel.profile.BaseLayerService; import com.getpcpanel.profile.LightingChangedToDefaultEvent; import com.getpcpanel.profile.Profile; diff --git a/src/main/java/com/getpcpanel/commands/command/AnalogBand.java b/src/main/java/com/getpcpanel/integration/analogbands/command/AnalogBand.java similarity index 96% rename from src/main/java/com/getpcpanel/commands/command/AnalogBand.java rename to src/main/java/com/getpcpanel/integration/analogbands/command/AnalogBand.java index ffb477ef..0fb3d5f4 100644 --- a/src/main/java/com/getpcpanel/commands/command/AnalogBand.java +++ b/src/main/java/com/getpcpanel/integration/analogbands/command/AnalogBand.java @@ -1,4 +1,4 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.analogbands.command; import javax.annotation.Nullable; diff --git a/src/main/java/com/getpcpanel/integration/analogbands/command/AnalogBandsCommandModule.java b/src/main/java/com/getpcpanel/integration/analogbands/command/AnalogBandsCommandModule.java new file mode 100644 index 00000000..737586af --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/analogbands/command/AnalogBandsCommandModule.java @@ -0,0 +1,21 @@ +package com.getpcpanel.integration.analogbands.command; + +import java.util.List; + +import com.getpcpanel.commands.CommandModule; +import com.getpcpanel.commands.command.Command; + +import jakarta.enterprise.context.ApplicationScoped; + +/** + * AnalogBands feature module: registers its own command types via the {@link com.getpcpanel.commands.CommandModule} + * SPI. Adding/removing a command touches only this package. + */ +@ApplicationScoped +public class AnalogBandsCommandModule implements CommandModule { + @Override + public List> commandTypes() { + return List.of( + CommandAnalogBands.class); + } +} diff --git a/src/main/java/com/getpcpanel/analogbands/BandTransition.java b/src/main/java/com/getpcpanel/integration/analogbands/command/BandTransition.java similarity index 91% rename from src/main/java/com/getpcpanel/analogbands/BandTransition.java rename to src/main/java/com/getpcpanel/integration/analogbands/command/BandTransition.java index 3a55fb7a..ef48bf11 100644 --- a/src/main/java/com/getpcpanel/analogbands/BandTransition.java +++ b/src/main/java/com/getpcpanel/integration/analogbands/command/BandTransition.java @@ -1,4 +1,4 @@ -package com.getpcpanel.analogbands; +package com.getpcpanel.integration.analogbands.command; /** * Result of feeding one analog reading to a {@link com.getpcpanel.commands.command.CommandAnalogBands}: the diff --git a/src/main/java/com/getpcpanel/commands/command/CommandAnalogBands.java b/src/main/java/com/getpcpanel/integration/analogbands/command/CommandAnalogBands.java similarity index 87% rename from src/main/java/com/getpcpanel/commands/command/CommandAnalogBands.java rename to src/main/java/com/getpcpanel/integration/analogbands/command/CommandAnalogBands.java index a7b272c0..7126f87f 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandAnalogBands.java +++ b/src/main/java/com/getpcpanel/integration/analogbands/command/CommandAnalogBands.java @@ -1,14 +1,20 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.analogbands.command; +import com.getpcpanel.commands.command.Command; +import com.getpcpanel.commands.command.DialAction; import java.util.List; import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -import com.getpcpanel.analogbands.AnalogBandColorService; -import com.getpcpanel.analogbands.BandTransition; +import com.getpcpanel.integration.analogbands.AnalogBandColorService; +import com.getpcpanel.integration.analogbands.command.BandTransition; import com.getpcpanel.commands.Commands; import com.getpcpanel.commands.PCPanelControlEvent; import com.getpcpanel.util.CdiHelper; @@ -35,6 +41,8 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("analogbands.ranges") +@CommandMeta(label = "Stepped switch (ranges)", category = CommandCategory.system, kinds = {CommandKind.dial}, icon = "sliders", legacyIds = {"com.getpcpanel.commands.command.CommandAnalogBands"}) public class CommandAnalogBands extends Command implements DialAction { private static final int MAX_RAW = 255; diff --git a/src/main/java/com/getpcpanel/hid/BrightnessService.java b/src/main/java/com/getpcpanel/integration/device/BrightnessService.java similarity index 95% rename from src/main/java/com/getpcpanel/hid/BrightnessService.java rename to src/main/java/com/getpcpanel/integration/device/BrightnessService.java index a577246e..060afae4 100644 --- a/src/main/java/com/getpcpanel/hid/BrightnessService.java +++ b/src/main/java/com/getpcpanel/integration/device/BrightnessService.java @@ -1,4 +1,6 @@ -package com.getpcpanel.hid; +package com.getpcpanel.integration.device; + +import com.getpcpanel.commands.DialValueCalculator; import java.util.Comparator; import java.util.List; @@ -8,7 +10,8 @@ import javax.annotation.Nullable; -import com.getpcpanel.commands.command.CommandBrightness; +import com.getpcpanel.integration.device.command.CommandBrightness; +import com.getpcpanel.device.DeviceHolder; import com.getpcpanel.profile.Profile; import com.getpcpanel.profile.SaveService; import com.getpcpanel.profile.dto.KnobSetting; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandBrightness.java b/src/main/java/com/getpcpanel/integration/device/command/CommandBrightness.java similarity index 66% rename from src/main/java/com/getpcpanel/commands/command/CommandBrightness.java rename to src/main/java/com/getpcpanel/integration/device/command/CommandBrightness.java index 56067b6a..a383f164 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandBrightness.java +++ b/src/main/java/com/getpcpanel/integration/device/command/CommandBrightness.java @@ -1,9 +1,15 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.device.command; +import com.getpcpanel.commands.command.Command; +import com.getpcpanel.commands.command.DialAction; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.util.CdiHelper; -import com.getpcpanel.hid.DeviceHolder; +import com.getpcpanel.device.DeviceHolder; import lombok.Getter; import lombok.ToString; @@ -12,6 +18,8 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("device.brightness") +@CommandMeta(label = "Brightness", category = CommandCategory.system, kinds = {CommandKind.dial}, icon = "sun", legacyIds = {"com.getpcpanel.commands.command.CommandBrightness"}) public class CommandBrightness extends Command implements DialAction { private final DialCommandParams dialParams; diff --git a/src/main/java/com/getpcpanel/integration/device/command/DeviceCommandModule.java b/src/main/java/com/getpcpanel/integration/device/command/DeviceCommandModule.java new file mode 100644 index 00000000..df877c6d --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/device/command/DeviceCommandModule.java @@ -0,0 +1,21 @@ +package com.getpcpanel.integration.device.command; + +import java.util.List; + +import com.getpcpanel.commands.CommandModule; +import com.getpcpanel.commands.command.Command; + +import jakarta.enterprise.context.ApplicationScoped; + +/** + * Device feature module: registers its own command types via the {@link com.getpcpanel.commands.CommandModule} + * SPI. Adding/removing a command touches only this package. + */ +@ApplicationScoped +public class DeviceCommandModule implements CommandModule { + @Override + public List> commandTypes() { + return List.of( + CommandBrightness.class); + } +} diff --git a/src/main/java/com/getpcpanel/discord/DiscordChangedEvent.java b/src/main/java/com/getpcpanel/integration/discord/DiscordChangedEvent.java similarity index 78% rename from src/main/java/com/getpcpanel/discord/DiscordChangedEvent.java rename to src/main/java/com/getpcpanel/integration/discord/DiscordChangedEvent.java index 0d85db02..6bc5eb09 100644 --- a/src/main/java/com/getpcpanel/discord/DiscordChangedEvent.java +++ b/src/main/java/com/getpcpanel/integration/discord/DiscordChangedEvent.java @@ -1,4 +1,4 @@ -package com.getpcpanel.discord; +package com.getpcpanel.integration.discord; /** Fired on the CDI event bus whenever Discord connection/auth state or voice state changes, so the UI re-reads it. */ public record DiscordChangedEvent() { diff --git a/src/main/java/com/getpcpanel/discord/DiscordCommandLabels.java b/src/main/java/com/getpcpanel/integration/discord/DiscordCommandLabels.java similarity index 88% rename from src/main/java/com/getpcpanel/discord/DiscordCommandLabels.java rename to src/main/java/com/getpcpanel/integration/discord/DiscordCommandLabels.java index 32d20502..c9a5dc4b 100644 --- a/src/main/java/com/getpcpanel/discord/DiscordCommandLabels.java +++ b/src/main/java/com/getpcpanel/integration/discord/DiscordCommandLabels.java @@ -1,6 +1,6 @@ -package com.getpcpanel.discord; +package com.getpcpanel.integration.discord; -import com.getpcpanel.cpp.MuteType; +import com.getpcpanel.integration.volume.platform.MuteType; /** * Shared label fragments for the Discord commands. Deliberately NOT in {@code com.getpcpanel.discord.command}: diff --git a/src/main/java/com/getpcpanel/mutecolor/DiscordMuteResolver.java b/src/main/java/com/getpcpanel/integration/discord/DiscordMuteResolver.java similarity index 83% rename from src/main/java/com/getpcpanel/mutecolor/DiscordMuteResolver.java rename to src/main/java/com/getpcpanel/integration/discord/DiscordMuteResolver.java index 4fc5e95d..5af18caf 100644 --- a/src/main/java/com/getpcpanel/mutecolor/DiscordMuteResolver.java +++ b/src/main/java/com/getpcpanel/integration/discord/DiscordMuteResolver.java @@ -1,5 +1,6 @@ -package com.getpcpanel.mutecolor; +package com.getpcpanel.integration.discord; +import com.getpcpanel.integration.volume.mutecolor.MuteStateResolver; import java.util.Optional; import javax.annotation.Nullable; @@ -7,11 +8,11 @@ import org.apache.commons.lang3.StringUtils; import com.getpcpanel.commands.Commands; -import com.getpcpanel.discord.DiscordService; -import com.getpcpanel.discord.command.CommandDiscordMute; -import com.getpcpanel.discord.command.CommandDiscordSelfDeafen; -import com.getpcpanel.discord.command.CommandDiscordSelfMute; -import com.getpcpanel.discord.command.CommandDiscordUserMute; +import com.getpcpanel.integration.discord.DiscordService; +import com.getpcpanel.integration.discord.command.CommandDiscordMute; +import com.getpcpanel.integration.discord.command.CommandDiscordSelfDeafen; +import com.getpcpanel.integration.discord.command.CommandDiscordSelfMute; +import com.getpcpanel.integration.discord.command.CommandDiscordUserMute; import dev.niels.discord.model.DiscordVoiceUser; import jakarta.enterprise.context.ApplicationScoped; @@ -25,7 +26,7 @@ * Discord client itself). */ @ApplicationScoped -public class DiscordMuteResolver implements MuteStateResolver { +class DiscordMuteResolver implements MuteStateResolver { private final DiscordService discord; @Inject diff --git a/src/main/java/com/getpcpanel/discord/DiscordOAuth.java b/src/main/java/com/getpcpanel/integration/discord/DiscordOAuth.java similarity index 98% rename from src/main/java/com/getpcpanel/discord/DiscordOAuth.java rename to src/main/java/com/getpcpanel/integration/discord/DiscordOAuth.java index a2f617cb..4adabafb 100644 --- a/src/main/java/com/getpcpanel/discord/DiscordOAuth.java +++ b/src/main/java/com/getpcpanel/integration/discord/DiscordOAuth.java @@ -1,4 +1,4 @@ -package com.getpcpanel.discord; +package com.getpcpanel.integration.discord; import java.net.URI; import java.net.URLEncoder; diff --git a/src/main/java/com/getpcpanel/discord/DiscordService.java b/src/main/java/com/getpcpanel/integration/discord/DiscordService.java similarity index 98% rename from src/main/java/com/getpcpanel/discord/DiscordService.java rename to src/main/java/com/getpcpanel/integration/discord/DiscordService.java index d72e2721..6944c6c6 100644 --- a/src/main/java/com/getpcpanel/discord/DiscordService.java +++ b/src/main/java/com/getpcpanel/integration/discord/DiscordService.java @@ -1,4 +1,4 @@ -package com.getpcpanel.discord; +package com.getpcpanel.integration.discord; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -12,14 +12,14 @@ import javax.annotation.Nullable; -import com.getpcpanel.cpp.MuteType; +import com.getpcpanel.integration.volume.platform.MuteType; import com.getpcpanel.profile.SaveService; import com.getpcpanel.profile.SaveService.SaveEvent; -import com.getpcpanel.profile.dto.DiscordAuth; -import com.getpcpanel.profile.dto.DiscordSeenUser; -import com.getpcpanel.profile.dto.DiscordSettings; -import com.getpcpanel.util.Debouncer; -import com.getpcpanel.util.ReconnectBackoff; +import com.getpcpanel.integration.discord.dto.DiscordAuth; +import com.getpcpanel.integration.discord.dto.DiscordSeenUser; +import com.getpcpanel.integration.discord.dto.DiscordSettings; +import com.getpcpanel.util.concurrent.Debouncer; +import com.getpcpanel.util.concurrent.ReconnectBackoff; import dev.niels.discord.DiscordRpcClient; import dev.niels.discord.DiscordRpcException; diff --git a/src/main/java/com/getpcpanel/discord/command/CommandDiscord.java b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscord.java similarity index 89% rename from src/main/java/com/getpcpanel/discord/command/CommandDiscord.java rename to src/main/java/com/getpcpanel/integration/discord/command/CommandDiscord.java index 6e6735f8..fd0bedc0 100644 --- a/src/main/java/com/getpcpanel/discord/command/CommandDiscord.java +++ b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscord.java @@ -1,7 +1,7 @@ -package com.getpcpanel.discord.command; +package com.getpcpanel.integration.discord.command; import com.getpcpanel.commands.command.Command; -import com.getpcpanel.discord.DiscordService; +import com.getpcpanel.integration.discord.DiscordService; import com.getpcpanel.util.CdiHelper; import lombok.Getter; diff --git a/src/main/java/com/getpcpanel/discord/command/CommandDiscordJoinVoice.java b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordJoinVoice.java similarity index 77% rename from src/main/java/com/getpcpanel/discord/command/CommandDiscordJoinVoice.java rename to src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordJoinVoice.java index dd192953..b7447604 100644 --- a/src/main/java/com/getpcpanel/discord/command/CommandDiscordJoinVoice.java +++ b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordJoinVoice.java @@ -1,10 +1,14 @@ -package com.getpcpanel.discord.command; +package com.getpcpanel.integration.discord.command; import org.apache.commons.lang3.StringUtils; import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.command.ButtonAction; @@ -16,6 +20,8 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("discord.join-voice") +@CommandMeta(label = "Discord — join voice", category = CommandCategory.integration, kinds = {CommandKind.button}, integration = "discord", icon = "plug", legacyIds = {"com.getpcpanel.discord.command.CommandDiscordJoinVoice"}) public final class CommandDiscordJoinVoice extends CommandDiscord implements ButtonAction { @Nullable private final String channelId; /** Cosmetic: the channel's name at configure time, for the button label (the id is what we join). */ diff --git a/src/main/java/com/getpcpanel/discord/command/CommandDiscordLeaveVoice.java b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordLeaveVoice.java similarity index 66% rename from src/main/java/com/getpcpanel/discord/command/CommandDiscordLeaveVoice.java rename to src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordLeaveVoice.java index 2296c8cb..c0aac658 100644 --- a/src/main/java/com/getpcpanel/discord/command/CommandDiscordLeaveVoice.java +++ b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordLeaveVoice.java @@ -1,8 +1,12 @@ -package com.getpcpanel.discord.command; +package com.getpcpanel.integration.discord.command; import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.command.ButtonAction; @@ -14,6 +18,8 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("discord.leave-voice") +@CommandMeta(label = "Discord — leave voice", category = CommandCategory.integration, kinds = {CommandKind.button}, integration = "discord", icon = "log-out", legacyIds = {"com.getpcpanel.discord.command.CommandDiscordLeaveVoice"}) public final class CommandDiscordLeaveVoice extends CommandDiscord implements ButtonAction { @Nullable private final String overlayText; diff --git a/src/main/java/com/getpcpanel/discord/command/CommandDiscordMute.java b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordMute.java similarity index 74% rename from src/main/java/com/getpcpanel/discord/command/CommandDiscordMute.java rename to src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordMute.java index 41ae4b38..9dd37097 100644 --- a/src/main/java/com/getpcpanel/discord/command/CommandDiscordMute.java +++ b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordMute.java @@ -1,14 +1,18 @@ -package com.getpcpanel.discord.command; +package com.getpcpanel.integration.discord.command; import org.apache.commons.lang3.StringUtils; import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.command.ButtonAction; -import com.getpcpanel.cpp.MuteType; -import com.getpcpanel.discord.DiscordCommandLabels; +import com.getpcpanel.integration.volume.platform.MuteType; +import com.getpcpanel.integration.discord.DiscordCommandLabels; import lombok.Getter; import lombok.ToString; @@ -21,6 +25,8 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("discord.mute") +@CommandMeta(label = "Discord — mute", category = CommandCategory.integration, kinds = {CommandKind.button}, integration = "discord", icon = "mic-off", legacyIds = {"com.getpcpanel.discord.command.CommandDiscordMute"}) public final class CommandDiscordMute extends CommandDiscord implements ButtonAction { public static final String SELF = "self"; diff --git a/src/main/java/com/getpcpanel/discord/command/CommandDiscordScreenShare.java b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordScreenShare.java similarity index 88% rename from src/main/java/com/getpcpanel/discord/command/CommandDiscordScreenShare.java rename to src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordScreenShare.java index 306f6547..13ad37e0 100644 --- a/src/main/java/com/getpcpanel/discord/command/CommandDiscordScreenShare.java +++ b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordScreenShare.java @@ -1,4 +1,4 @@ -package com.getpcpanel.discord.command; +package com.getpcpanel.integration.discord.command; import java.util.List; import java.util.Locale; @@ -9,9 +9,13 @@ import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.command.ButtonAction; -import com.getpcpanel.discord.DiscordService; +import com.getpcpanel.integration.discord.DiscordService; import com.getpcpanel.platform.IProcessHelper; import com.getpcpanel.util.CdiHelper; @@ -27,6 +31,8 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("discord.screen-share") +@CommandMeta(label = "Discord — screen share", category = CommandCategory.integration, kinds = {CommandKind.button}, integration = "discord", icon = "monitor", legacyIds = {"com.getpcpanel.discord.command.CommandDiscordScreenShare"}) public final class CommandDiscordScreenShare extends CommandDiscord implements ButtonAction { public enum Mode { SCREEN, PROCESS, FOCUS } diff --git a/src/main/java/com/getpcpanel/discord/command/CommandDiscordSelfDeafen.java b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordSelfDeafen.java similarity index 66% rename from src/main/java/com/getpcpanel/discord/command/CommandDiscordSelfDeafen.java rename to src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordSelfDeafen.java index 1045a55b..2db91429 100644 --- a/src/main/java/com/getpcpanel/discord/command/CommandDiscordSelfDeafen.java +++ b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordSelfDeafen.java @@ -1,12 +1,16 @@ -package com.getpcpanel.discord.command; +package com.getpcpanel.integration.discord.command; import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.command.ButtonAction; -import com.getpcpanel.cpp.MuteType; -import com.getpcpanel.discord.DiscordCommandLabels; +import com.getpcpanel.integration.volume.platform.MuteType; +import com.getpcpanel.integration.discord.DiscordCommandLabels; import lombok.Getter; import lombok.ToString; @@ -16,6 +20,8 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("discord.self-deafen") +@CommandMeta(label = "Discord — deafen self", category = CommandCategory.integration, kinds = {CommandKind.button}, integration = "discord", icon = "volume-x", legacyIds = {"com.getpcpanel.discord.command.CommandDiscordSelfDeafen"}) public final class CommandDiscordSelfDeafen extends CommandDiscord implements ButtonAction { private final MuteType muteType; @Nullable private final String overlayText; diff --git a/src/main/java/com/getpcpanel/discord/command/CommandDiscordSelfInputVolume.java b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordSelfInputVolume.java similarity index 87% rename from src/main/java/com/getpcpanel/discord/command/CommandDiscordSelfInputVolume.java rename to src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordSelfInputVolume.java index b71cedcb..56a87b48 100644 --- a/src/main/java/com/getpcpanel/discord/command/CommandDiscordSelfInputVolume.java +++ b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordSelfInputVolume.java @@ -1,8 +1,9 @@ -package com.getpcpanel.discord.command; +package com.getpcpanel.integration.discord.command; import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.command.DialAction; @@ -14,6 +15,7 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("com.getpcpanel.discord.command.CommandDiscordSelfInputVolume") public final class CommandDiscordSelfInputVolume extends CommandDiscord implements DialAction { @Nullable private final DialCommandParams dialParams; private final boolean unmuteOnChange; diff --git a/src/main/java/com/getpcpanel/discord/command/CommandDiscordSelfMute.java b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordSelfMute.java similarity index 81% rename from src/main/java/com/getpcpanel/discord/command/CommandDiscordSelfMute.java rename to src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordSelfMute.java index 73cf8d1d..0e53138e 100644 --- a/src/main/java/com/getpcpanel/discord/command/CommandDiscordSelfMute.java +++ b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordSelfMute.java @@ -1,12 +1,13 @@ -package com.getpcpanel.discord.command; +package com.getpcpanel.integration.discord.command; import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.command.ButtonAction; -import com.getpcpanel.cpp.MuteType; -import com.getpcpanel.discord.DiscordCommandLabels; +import com.getpcpanel.integration.volume.platform.MuteType; +import com.getpcpanel.integration.discord.DiscordCommandLabels; import lombok.Getter; import lombok.ToString; @@ -16,6 +17,7 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("com.getpcpanel.discord.command.CommandDiscordSelfMute") public final class CommandDiscordSelfMute extends CommandDiscord implements ButtonAction { private final MuteType muteType; @Nullable private final String overlayText; diff --git a/src/main/java/com/getpcpanel/discord/command/CommandDiscordSelfOutputVolume.java b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordSelfOutputVolume.java similarity index 88% rename from src/main/java/com/getpcpanel/discord/command/CommandDiscordSelfOutputVolume.java rename to src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordSelfOutputVolume.java index f1b5f0aa..01909e88 100644 --- a/src/main/java/com/getpcpanel/discord/command/CommandDiscordSelfOutputVolume.java +++ b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordSelfOutputVolume.java @@ -1,8 +1,9 @@ -package com.getpcpanel.discord.command; +package com.getpcpanel.integration.discord.command; import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.command.DialAction; @@ -14,6 +15,7 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("com.getpcpanel.discord.command.CommandDiscordSelfOutputVolume") public final class CommandDiscordSelfOutputVolume extends CommandDiscord implements DialAction { @Nullable private final DialCommandParams dialParams; private final boolean undeafenOnChange; diff --git a/src/main/java/com/getpcpanel/discord/command/CommandDiscordToggleVideo.java b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordToggleVideo.java similarity index 69% rename from src/main/java/com/getpcpanel/discord/command/CommandDiscordToggleVideo.java rename to src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordToggleVideo.java index a0fd77e2..70dbb83c 100644 --- a/src/main/java/com/getpcpanel/discord/command/CommandDiscordToggleVideo.java +++ b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordToggleVideo.java @@ -1,8 +1,12 @@ -package com.getpcpanel.discord.command; +package com.getpcpanel.integration.discord.command; import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.command.ButtonAction; @@ -14,6 +18,8 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("discord.toggle-video") +@CommandMeta(label = "Discord — toggle camera", category = CommandCategory.integration, kinds = {CommandKind.button}, integration = "discord", icon = "film", legacyIds = {"com.getpcpanel.discord.command.CommandDiscordToggleVideo"}) public final class CommandDiscordToggleVideo extends CommandDiscord implements ButtonAction { @Nullable private final String overlayText; diff --git a/src/main/java/com/getpcpanel/discord/command/CommandDiscordUserMute.java b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordUserMute.java similarity index 85% rename from src/main/java/com/getpcpanel/discord/command/CommandDiscordUserMute.java rename to src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordUserMute.java index 316c0cdd..1454b316 100644 --- a/src/main/java/com/getpcpanel/discord/command/CommandDiscordUserMute.java +++ b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordUserMute.java @@ -1,14 +1,15 @@ -package com.getpcpanel.discord.command; +package com.getpcpanel.integration.discord.command; import org.apache.commons.lang3.StringUtils; import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.command.ButtonAction; -import com.getpcpanel.cpp.MuteType; -import com.getpcpanel.discord.DiscordCommandLabels; +import com.getpcpanel.integration.volume.platform.MuteType; +import com.getpcpanel.integration.discord.DiscordCommandLabels; import lombok.Getter; import lombok.ToString; @@ -18,6 +19,7 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("com.getpcpanel.discord.command.CommandDiscordUserMute") public final class CommandDiscordUserMute extends CommandDiscord implements ButtonAction { @Nullable private final String username; private final MuteType muteType; diff --git a/src/main/java/com/getpcpanel/discord/command/CommandDiscordUserVolume.java b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordUserVolume.java similarity index 89% rename from src/main/java/com/getpcpanel/discord/command/CommandDiscordUserVolume.java rename to src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordUserVolume.java index 7387162d..8e31670c 100644 --- a/src/main/java/com/getpcpanel/discord/command/CommandDiscordUserVolume.java +++ b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordUserVolume.java @@ -1,10 +1,11 @@ -package com.getpcpanel.discord.command; +package com.getpcpanel.integration.discord.command; import org.apache.commons.lang3.StringUtils; import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.command.DialAction; @@ -16,6 +17,7 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("com.getpcpanel.discord.command.CommandDiscordUserVolume") public final class CommandDiscordUserVolume extends CommandDiscord implements DialAction { @Nullable private final String username; @Nullable private final DialCommandParams dialParams; diff --git a/src/main/java/com/getpcpanel/discord/command/CommandDiscordVolume.java b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordVolume.java similarity index 82% rename from src/main/java/com/getpcpanel/discord/command/CommandDiscordVolume.java rename to src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordVolume.java index d25b76da..e5b51781 100644 --- a/src/main/java/com/getpcpanel/discord/command/CommandDiscordVolume.java +++ b/src/main/java/com/getpcpanel/integration/discord/command/CommandDiscordVolume.java @@ -1,10 +1,14 @@ -package com.getpcpanel.discord.command; +package com.getpcpanel.integration.discord.command; import org.apache.commons.lang3.StringUtils; import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.command.DialAction; @@ -21,6 +25,8 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("discord.volume") +@CommandMeta(label = "Discord — volume", category = CommandCategory.integration, kinds = {CommandKind.dial}, integration = "discord", icon = "volume", legacyIds = {"com.getpcpanel.discord.command.CommandDiscordVolume"}) public final class CommandDiscordVolume extends CommandDiscord implements DialAction { public static final String MIC = "mic"; public static final String OUTPUT = "output"; diff --git a/src/main/java/com/getpcpanel/integration/discord/command/DiscordCommandModule.java b/src/main/java/com/getpcpanel/integration/discord/command/DiscordCommandModule.java new file mode 100644 index 00000000..9b911ae2 --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/discord/command/DiscordCommandModule.java @@ -0,0 +1,32 @@ +package com.getpcpanel.integration.discord.command; + +import java.util.List; + +import com.getpcpanel.commands.CommandModule; +import com.getpcpanel.commands.command.Command; + +import jakarta.enterprise.context.ApplicationScoped; + +/** + * Discord feature module: registers its own command types via the + * {@link com.getpcpanel.commands.CommandModule} SPI. Adding/removing a command touches only this package. + */ +@ApplicationScoped +public class DiscordCommandModule implements CommandModule { + @Override + public List> commandTypes() { + return List.of( + CommandDiscordJoinVoice.class, + CommandDiscordLeaveVoice.class, + CommandDiscordMute.class, + CommandDiscordScreenShare.class, + CommandDiscordSelfDeafen.class, + CommandDiscordSelfInputVolume.class, + CommandDiscordSelfMute.class, + CommandDiscordSelfOutputVolume.class, + CommandDiscordToggleVideo.class, + CommandDiscordUserMute.class, + CommandDiscordUserVolume.class, + CommandDiscordVolume.class); + } +} diff --git a/src/main/java/com/getpcpanel/profile/dto/DiscordAuth.java b/src/main/java/com/getpcpanel/integration/discord/dto/DiscordAuth.java similarity index 96% rename from src/main/java/com/getpcpanel/profile/dto/DiscordAuth.java rename to src/main/java/com/getpcpanel/integration/discord/dto/DiscordAuth.java index f6278f33..bcdf9d5c 100644 --- a/src/main/java/com/getpcpanel/profile/dto/DiscordAuth.java +++ b/src/main/java/com/getpcpanel/integration/discord/dto/DiscordAuth.java @@ -1,4 +1,4 @@ -package com.getpcpanel.profile.dto; +package com.getpcpanel.integration.discord.dto; import javax.annotation.Nullable; diff --git a/src/main/java/com/getpcpanel/profile/dto/DiscordSeenUser.java b/src/main/java/com/getpcpanel/integration/discord/dto/DiscordSeenUser.java similarity index 94% rename from src/main/java/com/getpcpanel/profile/dto/DiscordSeenUser.java rename to src/main/java/com/getpcpanel/integration/discord/dto/DiscordSeenUser.java index d95bde03..20355622 100644 --- a/src/main/java/com/getpcpanel/profile/dto/DiscordSeenUser.java +++ b/src/main/java/com/getpcpanel/integration/discord/dto/DiscordSeenUser.java @@ -1,4 +1,4 @@ -package com.getpcpanel.profile.dto; +package com.getpcpanel.integration.discord.dto; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/src/main/java/com/getpcpanel/profile/dto/DiscordSettings.java b/src/main/java/com/getpcpanel/integration/discord/dto/DiscordSettings.java similarity index 97% rename from src/main/java/com/getpcpanel/profile/dto/DiscordSettings.java rename to src/main/java/com/getpcpanel/integration/discord/dto/DiscordSettings.java index f136fdeb..1594b383 100644 --- a/src/main/java/com/getpcpanel/profile/dto/DiscordSettings.java +++ b/src/main/java/com/getpcpanel/integration/discord/dto/DiscordSettings.java @@ -1,4 +1,4 @@ -package com.getpcpanel.profile.dto; +package com.getpcpanel.integration.discord.dto; import org.apache.commons.lang3.StringUtils; diff --git a/src/main/java/com/getpcpanel/rest/discord/DiscordResource.java b/src/main/java/com/getpcpanel/integration/discord/rest/DiscordResource.java similarity index 87% rename from src/main/java/com/getpcpanel/rest/discord/DiscordResource.java rename to src/main/java/com/getpcpanel/integration/discord/rest/DiscordResource.java index 8bf01a4a..e5f6bd81 100644 --- a/src/main/java/com/getpcpanel/rest/discord/DiscordResource.java +++ b/src/main/java/com/getpcpanel/integration/discord/rest/DiscordResource.java @@ -1,12 +1,12 @@ -package com.getpcpanel.rest.discord; +package com.getpcpanel.integration.discord.rest; import java.util.List; import java.util.concurrent.CompletionStage; -import com.getpcpanel.discord.DiscordService; -import com.getpcpanel.rest.discord.dto.DiscordStatusDto; -import com.getpcpanel.rest.discord.dto.DiscordUserDto; -import com.getpcpanel.rest.discord.dto.DiscordVoiceChannelDto; +import com.getpcpanel.integration.discord.DiscordService; +import com.getpcpanel.integration.discord.rest.dto.DiscordStatusDto; +import com.getpcpanel.integration.discord.rest.dto.DiscordUserDto; +import com.getpcpanel.integration.discord.rest.dto.DiscordVoiceChannelDto; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; diff --git a/src/main/java/com/getpcpanel/rest/discord/dto/DiscordStatusDto.java b/src/main/java/com/getpcpanel/integration/discord/rest/dto/DiscordStatusDto.java similarity index 92% rename from src/main/java/com/getpcpanel/rest/discord/dto/DiscordStatusDto.java rename to src/main/java/com/getpcpanel/integration/discord/rest/dto/DiscordStatusDto.java index 7a585465..90245302 100644 --- a/src/main/java/com/getpcpanel/rest/discord/dto/DiscordStatusDto.java +++ b/src/main/java/com/getpcpanel/integration/discord/rest/dto/DiscordStatusDto.java @@ -1,8 +1,8 @@ -package com.getpcpanel.rest.discord.dto; +package com.getpcpanel.integration.discord.rest.dto; import javax.annotation.Nullable; -import com.getpcpanel.discord.DiscordService; +import com.getpcpanel.integration.discord.DiscordService; import io.quarkus.runtime.annotations.RegisterForReflection; diff --git a/src/main/java/com/getpcpanel/rest/discord/dto/DiscordUserDto.java b/src/main/java/com/getpcpanel/integration/discord/rest/dto/DiscordUserDto.java similarity index 95% rename from src/main/java/com/getpcpanel/rest/discord/dto/DiscordUserDto.java rename to src/main/java/com/getpcpanel/integration/discord/rest/dto/DiscordUserDto.java index 2fdb86b6..9225f131 100644 --- a/src/main/java/com/getpcpanel/rest/discord/dto/DiscordUserDto.java +++ b/src/main/java/com/getpcpanel/integration/discord/rest/dto/DiscordUserDto.java @@ -1,4 +1,4 @@ -package com.getpcpanel.rest.discord.dto; +package com.getpcpanel.integration.discord.rest.dto; import java.util.HashSet; import java.util.LinkedHashMap; @@ -7,7 +7,7 @@ import org.apache.commons.lang3.StringUtils; -import com.getpcpanel.discord.DiscordService; +import com.getpcpanel.integration.discord.DiscordService; import io.quarkus.runtime.annotations.RegisterForReflection; diff --git a/src/main/java/com/getpcpanel/rest/discord/dto/DiscordVoiceChannelDto.java b/src/main/java/com/getpcpanel/integration/discord/rest/dto/DiscordVoiceChannelDto.java similarity index 90% rename from src/main/java/com/getpcpanel/rest/discord/dto/DiscordVoiceChannelDto.java rename to src/main/java/com/getpcpanel/integration/discord/rest/dto/DiscordVoiceChannelDto.java index 8c6648ef..c69dd7f9 100644 --- a/src/main/java/com/getpcpanel/rest/discord/dto/DiscordVoiceChannelDto.java +++ b/src/main/java/com/getpcpanel/integration/discord/rest/dto/DiscordVoiceChannelDto.java @@ -1,4 +1,4 @@ -package com.getpcpanel.rest.discord.dto; +package com.getpcpanel.integration.discord.rest.dto; import io.quarkus.runtime.annotations.RegisterForReflection; diff --git a/src/main/java/com/getpcpanel/homeassistant/HaActionYaml.java b/src/main/java/com/getpcpanel/integration/homeassistant/HaActionYaml.java similarity index 97% rename from src/main/java/com/getpcpanel/homeassistant/HaActionYaml.java rename to src/main/java/com/getpcpanel/integration/homeassistant/HaActionYaml.java index 4ea72927..85eb5f91 100644 --- a/src/main/java/com/getpcpanel/homeassistant/HaActionYaml.java +++ b/src/main/java/com/getpcpanel/integration/homeassistant/HaActionYaml.java @@ -1,4 +1,4 @@ -package com.getpcpanel.homeassistant; +package com.getpcpanel.integration.homeassistant; import java.util.LinkedHashMap; import java.util.List; @@ -24,7 +24,7 @@ * Java types — which is both safer and friendlier to the native image. */ @Log4j2 -public final class HaActionYaml { +final class HaActionYaml { private HaActionYaml() { } diff --git a/src/main/java/com/getpcpanel/homeassistant/HomeAssistantClient.java b/src/main/java/com/getpcpanel/integration/homeassistant/HomeAssistantClient.java similarity index 96% rename from src/main/java/com/getpcpanel/homeassistant/HomeAssistantClient.java rename to src/main/java/com/getpcpanel/integration/homeassistant/HomeAssistantClient.java index d21d8da4..8dc421d4 100644 --- a/src/main/java/com/getpcpanel/homeassistant/HomeAssistantClient.java +++ b/src/main/java/com/getpcpanel/integration/homeassistant/HomeAssistantClient.java @@ -1,4 +1,4 @@ -package com.getpcpanel.homeassistant; +package com.getpcpanel.integration.homeassistant; import java.net.URI; import java.net.http.HttpClient; @@ -8,7 +8,7 @@ import java.util.Map; import com.fasterxml.jackson.databind.ObjectMapper; -import com.getpcpanel.homeassistant.dto.HomeAssistantServer; +import com.getpcpanel.integration.homeassistant.dto.HomeAssistantServer; import com.getpcpanel.util.SharedHttpClient; import lombok.Getter; @@ -23,7 +23,7 @@ * @see Home Assistant REST API */ @Log4j2 -public class HomeAssistantClient { +class HomeAssistantClient { private static final Duration REQUEST_TIMEOUT = Duration.ofSeconds(10); @Getter private final HomeAssistantServer server; diff --git a/src/main/java/com/getpcpanel/homeassistant/HomeAssistantService.java b/src/main/java/com/getpcpanel/integration/homeassistant/HomeAssistantService.java similarity index 95% rename from src/main/java/com/getpcpanel/homeassistant/HomeAssistantService.java rename to src/main/java/com/getpcpanel/integration/homeassistant/HomeAssistantService.java index 398a884d..f82a6355 100644 --- a/src/main/java/com/getpcpanel/homeassistant/HomeAssistantService.java +++ b/src/main/java/com/getpcpanel/integration/homeassistant/HomeAssistantService.java @@ -1,4 +1,4 @@ -package com.getpcpanel.homeassistant; +package com.getpcpanel.integration.homeassistant; import java.util.ArrayList; import java.util.List; @@ -11,11 +11,11 @@ import org.apache.commons.lang3.StringUtils; import com.fasterxml.jackson.databind.ObjectMapper; -import com.getpcpanel.homeassistant.dto.HomeAssistantServer; -import com.getpcpanel.homeassistant.dto.HomeAssistantServerStatus; +import com.getpcpanel.integration.homeassistant.dto.HomeAssistantServer; +import com.getpcpanel.integration.homeassistant.dto.HomeAssistantServerStatus; import com.getpcpanel.profile.SaveService; import com.getpcpanel.profile.SaveService.SaveEvent; -import com.getpcpanel.util.Debouncer; +import com.getpcpanel.util.concurrent.Debouncer; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Observes; diff --git a/src/main/java/com/getpcpanel/homeassistant/command/CommandHomeAssistant.java b/src/main/java/com/getpcpanel/integration/homeassistant/command/CommandHomeAssistant.java similarity index 85% rename from src/main/java/com/getpcpanel/homeassistant/command/CommandHomeAssistant.java rename to src/main/java/com/getpcpanel/integration/homeassistant/command/CommandHomeAssistant.java index d5c04658..3fe08db0 100644 --- a/src/main/java/com/getpcpanel/homeassistant/command/CommandHomeAssistant.java +++ b/src/main/java/com/getpcpanel/integration/homeassistant/command/CommandHomeAssistant.java @@ -1,9 +1,9 @@ -package com.getpcpanel.homeassistant.command; +package com.getpcpanel.integration.homeassistant.command; import javax.annotation.Nullable; import com.getpcpanel.commands.command.Command; -import com.getpcpanel.homeassistant.HomeAssistantService; +import com.getpcpanel.integration.homeassistant.HomeAssistantService; import com.getpcpanel.util.CdiHelper; import lombok.Getter; diff --git a/src/main/java/com/getpcpanel/homeassistant/command/CommandHomeAssistantAction.java b/src/main/java/com/getpcpanel/integration/homeassistant/command/CommandHomeAssistantAction.java similarity index 74% rename from src/main/java/com/getpcpanel/homeassistant/command/CommandHomeAssistantAction.java rename to src/main/java/com/getpcpanel/integration/homeassistant/command/CommandHomeAssistantAction.java index 9b5b00cd..1a55f1b1 100644 --- a/src/main/java/com/getpcpanel/homeassistant/command/CommandHomeAssistantAction.java +++ b/src/main/java/com/getpcpanel/integration/homeassistant/command/CommandHomeAssistantAction.java @@ -1,10 +1,14 @@ -package com.getpcpanel.homeassistant.command; +package com.getpcpanel.integration.homeassistant.command; import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.command.ButtonAction; @@ -19,6 +23,8 @@ */ @Getter @ToString(callSuper = true) +@JsonTypeName("homeassistant.action") +@CommandMeta(label = "Home Assistant — perform action", category = CommandCategory.integration, kinds = {CommandKind.button}, integration = "homeassistant", icon = "zap", legacyIds = {"com.getpcpanel.homeassistant.command.CommandHomeAssistantAction"}) public class CommandHomeAssistantAction extends CommandHomeAssistant implements ButtonAction { private final String action; @Nullable private final String overlayText; diff --git a/src/main/java/com/getpcpanel/homeassistant/command/CommandHomeAssistantValue.java b/src/main/java/com/getpcpanel/integration/homeassistant/command/CommandHomeAssistantValue.java similarity index 84% rename from src/main/java/com/getpcpanel/homeassistant/command/CommandHomeAssistantValue.java rename to src/main/java/com/getpcpanel/integration/homeassistant/command/CommandHomeAssistantValue.java index 5f473361..18cdfb6a 100644 --- a/src/main/java/com/getpcpanel/homeassistant/command/CommandHomeAssistantValue.java +++ b/src/main/java/com/getpcpanel/integration/homeassistant/command/CommandHomeAssistantValue.java @@ -1,10 +1,14 @@ -package com.getpcpanel.homeassistant.command; +package com.getpcpanel.integration.homeassistant.command; import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.command.DialAction; import com.getpcpanel.util.ValueInterpolator; @@ -22,6 +26,8 @@ */ @Getter @ToString(callSuper = true) +@JsonTypeName("homeassistant.value") +@CommandMeta(label = "Home Assistant — set value", category = CommandCategory.integration, kinds = {CommandKind.dial}, integration = "homeassistant", icon = "sliders", legacyIds = {"com.getpcpanel.homeassistant.command.CommandHomeAssistantValue"}) public class CommandHomeAssistantValue extends CommandHomeAssistant implements DialAction { private final String action; @Nullable private final Double min; diff --git a/src/main/java/com/getpcpanel/integration/homeassistant/command/HomeAssistantCommandModule.java b/src/main/java/com/getpcpanel/integration/homeassistant/command/HomeAssistantCommandModule.java new file mode 100644 index 00000000..d758ce99 --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/homeassistant/command/HomeAssistantCommandModule.java @@ -0,0 +1,22 @@ +package com.getpcpanel.integration.homeassistant.command; + +import java.util.List; + +import com.getpcpanel.commands.CommandModule; +import com.getpcpanel.commands.command.Command; + +import jakarta.enterprise.context.ApplicationScoped; + +/** + * HomeAssistant feature module: registers its own command types via the + * {@link com.getpcpanel.commands.CommandModule} SPI. Adding/removing a command touches only this package. + */ +@ApplicationScoped +public class HomeAssistantCommandModule implements CommandModule { + @Override + public List> commandTypes() { + return List.of( + CommandHomeAssistantAction.class, + CommandHomeAssistantValue.class); + } +} diff --git a/src/main/java/com/getpcpanel/homeassistant/dto/HomeAssistantServer.java b/src/main/java/com/getpcpanel/integration/homeassistant/dto/HomeAssistantServer.java similarity index 88% rename from src/main/java/com/getpcpanel/homeassistant/dto/HomeAssistantServer.java rename to src/main/java/com/getpcpanel/integration/homeassistant/dto/HomeAssistantServer.java index 0a30227a..43ec9dac 100644 --- a/src/main/java/com/getpcpanel/homeassistant/dto/HomeAssistantServer.java +++ b/src/main/java/com/getpcpanel/integration/homeassistant/dto/HomeAssistantServer.java @@ -1,4 +1,4 @@ -package com.getpcpanel.homeassistant.dto; +package com.getpcpanel.integration.homeassistant.dto; import javax.annotation.Nullable; diff --git a/src/main/java/com/getpcpanel/homeassistant/dto/HomeAssistantServerStatus.java b/src/main/java/com/getpcpanel/integration/homeassistant/dto/HomeAssistantServerStatus.java similarity index 86% rename from src/main/java/com/getpcpanel/homeassistant/dto/HomeAssistantServerStatus.java rename to src/main/java/com/getpcpanel/integration/homeassistant/dto/HomeAssistantServerStatus.java index 0102fd9e..b02f9cba 100644 --- a/src/main/java/com/getpcpanel/homeassistant/dto/HomeAssistantServerStatus.java +++ b/src/main/java/com/getpcpanel/integration/homeassistant/dto/HomeAssistantServerStatus.java @@ -1,4 +1,4 @@ -package com.getpcpanel.homeassistant.dto; +package com.getpcpanel.integration.homeassistant.dto; import javax.annotation.Nullable; diff --git a/src/main/java/com/getpcpanel/homeassistant/rest/HomeAssistantResource.java b/src/main/java/com/getpcpanel/integration/homeassistant/rest/HomeAssistantResource.java similarity index 84% rename from src/main/java/com/getpcpanel/homeassistant/rest/HomeAssistantResource.java rename to src/main/java/com/getpcpanel/integration/homeassistant/rest/HomeAssistantResource.java index 8a1bc24c..ad5bf221 100644 --- a/src/main/java/com/getpcpanel/homeassistant/rest/HomeAssistantResource.java +++ b/src/main/java/com/getpcpanel/integration/homeassistant/rest/HomeAssistantResource.java @@ -1,10 +1,10 @@ -package com.getpcpanel.homeassistant.rest; +package com.getpcpanel.integration.homeassistant.rest; import java.util.List; import java.util.Map; -import com.getpcpanel.homeassistant.HomeAssistantService; -import com.getpcpanel.homeassistant.dto.HomeAssistantServerStatus; +import com.getpcpanel.integration.homeassistant.HomeAssistantService; +import com.getpcpanel.integration.homeassistant.dto.HomeAssistantServerStatus; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; diff --git a/src/main/java/com/getpcpanel/integration/keyboard/Keyboard.java b/src/main/java/com/getpcpanel/integration/keyboard/Keyboard.java new file mode 100644 index 00000000..d78f6927 --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/keyboard/Keyboard.java @@ -0,0 +1,27 @@ +package com.getpcpanel.integration.keyboard; + +import com.getpcpanel.integration.keyboard.command.CommandMedia.VolumeButton; + +/** + * Cross-platform keyboard / media-key synthesis. Exactly one implementation is present in a given + * build, selected by the {@code @WindowsBuild}/{@code @MacBuild}/{@code @LinuxBuild} CDI stereotypes, + * so callers simply inject {@code Keyboard} (or resolve it via {@code CdiHelper.getBean(Keyboard.class)}) + * and never branch on the operating system. The per-OS backends are package-private implementation + * details under {@code platform/}: macOS via CoreGraphics {@code CGEvent}s, Windows via + * {@code User32.SendInput}, Linux via the X11 {@code XTEST} extension — all native (JNA) paths so the + * GraalVM native image never needs {@link java.awt.Robot} / the AWT windowing toolkit. + */ +public interface Keyboard { + /** Presses a single "{@code modifier+modifier+key}" combination (e.g. {@code ctrl+shift+A}). */ + void executeKeyStroke(String input); + + /** Types arbitrary text out character-by-character. */ + void typeText(String text); + + /** + * Sends a multimedia key (play/pause, next, prev, stop, mute). {@code spotify} requests targeting + * Spotify specifically where the platform supports it (Windows {@code WM_APPCOMMAND} / macOS + * AppleScript); on Linux the desktop routes the global media key to the active player regardless. + */ + void sendMediaKey(VolumeButton button, boolean spotify); +} diff --git a/src/main/java/com/getpcpanel/commands/command/CommandKeystroke.java b/src/main/java/com/getpcpanel/integration/keyboard/command/CommandKeystroke.java similarity index 67% rename from src/main/java/com/getpcpanel/commands/command/CommandKeystroke.java rename to src/main/java/com/getpcpanel/integration/keyboard/command/CommandKeystroke.java index f05e93fc..5878ecbb 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandKeystroke.java +++ b/src/main/java/com/getpcpanel/integration/keyboard/command/CommandKeystroke.java @@ -1,10 +1,17 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.keyboard.command; +import com.getpcpanel.commands.command.Command; +import com.getpcpanel.commands.command.ButtonAction; import org.apache.commons.lang3.StringUtils; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; -import com.getpcpanel.commands.KeyMacro; +import com.getpcpanel.integration.keyboard.Keyboard; +import com.getpcpanel.util.CdiHelper; import lombok.Getter; import lombok.ToString; @@ -14,7 +21,7 @@ * Button action that synthesises keyboard input. Two flavours, selected by {@link #type}: *
    *
  • {@link KeystrokeType#KEY} — presses a single key combination such as {@code ctrl+shift+A} - * (the cross-platform "{@code modifier+modifier+key}" format parsed by {@link KeyMacro}).
  • + * (the cross-platform "{@code modifier+modifier+key}" format the {@link Keyboard} backend parses). *
  • {@link KeystrokeType#TEXT} — types an arbitrary string character-by-character.
  • *
* Profiles saved before this field existed deserialize with a {@code null} {@code type}, which @@ -23,6 +30,8 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("keyboard.keystroke") +@CommandMeta(label = "Keystroke", category = CommandCategory.system, kinds = {CommandKind.button}, icon = "keyboard", legacyIds = {"com.getpcpanel.commands.command.CommandKeystroke"}) public class CommandKeystroke extends Command implements ButtonAction { public enum KeystrokeType { /** Single key combination (modifiers + one key). */ @@ -46,10 +55,11 @@ public CommandKeystroke(@JsonProperty("type") KeystrokeType type, @Override public void execute() { + var keyboard = CdiHelper.getBean(Keyboard.class); if (type == KeystrokeType.TEXT) { - KeyMacro.typeText(text); + keyboard.typeText(text); } else { - KeyMacro.executeKeyStroke(keystroke); + keyboard.executeKeyStroke(keystroke); } } diff --git a/src/main/java/com/getpcpanel/integration/keyboard/command/CommandMedia.java b/src/main/java/com/getpcpanel/integration/keyboard/command/CommandMedia.java new file mode 100644 index 00000000..8ed6b0c9 --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/keyboard/command/CommandMedia.java @@ -0,0 +1,63 @@ +package com.getpcpanel.integration.keyboard.command; + +import java.util.Optional; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.command.ButtonAction; +import com.getpcpanel.commands.command.Command; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; +import com.getpcpanel.integration.keyboard.Keyboard; +import com.getpcpanel.util.CdiHelper; + +import lombok.Getter; +import lombok.ToString; + +/** + * Button action that presses a multimedia key (play/pause, next, prev, stop, mute), optionally + * targeting Spotify. All platform behaviour lives in the {@link Keyboard} backend — this command just + * carries the chosen button and forwards it. + */ +@Getter +@ToString(callSuper = true) +@JsonTypeName("keyboard.media") +@CommandMeta(label = "Media", category = CommandCategory.system, kinds = {CommandKind.button}, icon = "play", legacyIds = {"com.getpcpanel.commands.command.CommandMedia"}) +public class CommandMedia extends Command implements ButtonAction { + private final VolumeButton button; + private final boolean spotify; + + /** Cross-platform media-button identifier; each {@link Keyboard} backend maps it to its native key. */ + public enum VolumeButton { + mute, next, prev, stop, playPause; + + public static Optional tryValueOf(String name) { + try { + return Optional.of(valueOf(name)); + } catch (IllegalArgumentException e) { + return Optional.empty(); + } + } + } + + @JsonCreator + public CommandMedia(@JsonProperty("button") VolumeButton button, @JsonProperty("spotify") boolean spotify) { + this.button = button; + this.spotify = spotify; + } + + @Override + public void execute() { + if (button == null) { + return; + } + CdiHelper.getBean(Keyboard.class).sendMediaKey(button, spotify); + } + + @Override + public String buildLabel() { + return button + (spotify ? " (Spotify)" : ""); + } +} diff --git a/src/main/java/com/getpcpanel/integration/keyboard/command/KeyboardCommandModule.java b/src/main/java/com/getpcpanel/integration/keyboard/command/KeyboardCommandModule.java new file mode 100644 index 00000000..bdafb8e5 --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/keyboard/command/KeyboardCommandModule.java @@ -0,0 +1,22 @@ +package com.getpcpanel.integration.keyboard.command; + +import java.util.List; + +import com.getpcpanel.commands.CommandModule; +import com.getpcpanel.commands.command.Command; + +import jakarta.enterprise.context.ApplicationScoped; + +/** + * Keyboard feature module: registers its own command types via the {@link com.getpcpanel.commands.CommandModule} + * SPI. Adding/removing a command touches only this package. + */ +@ApplicationScoped +public class KeyboardCommandModule implements CommandModule { + @Override + public List> commandTypes() { + return List.of( + CommandKeystroke.class, + CommandMedia.class); + } +} diff --git a/src/main/java/com/getpcpanel/cpp/linux/LinuxKeyboard.java b/src/main/java/com/getpcpanel/integration/keyboard/platform/linux/LinuxKeyboard.java similarity index 94% rename from src/main/java/com/getpcpanel/cpp/linux/LinuxKeyboard.java rename to src/main/java/com/getpcpanel/integration/keyboard/platform/linux/LinuxKeyboard.java index f727518c..33fc966f 100644 --- a/src/main/java/com/getpcpanel/cpp/linux/LinuxKeyboard.java +++ b/src/main/java/com/getpcpanel/integration/keyboard/platform/linux/LinuxKeyboard.java @@ -1,32 +1,37 @@ -package com.getpcpanel.cpp.linux; +package com.getpcpanel.integration.keyboard.platform.linux; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; -import com.getpcpanel.commands.command.CommandMedia.VolumeButton; +import com.getpcpanel.integration.keyboard.Keyboard; +import com.getpcpanel.integration.keyboard.command.CommandMedia.VolumeButton; +import com.getpcpanel.platform.LinuxBuild; import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.NativeLong; import com.sun.jna.Pointer; import com.sun.jna.ptr.IntByReference; +import jakarta.enterprise.context.ApplicationScoped; import lombok.extern.log4j.Log4j2; /** - * Synthesises keystrokes on Linux through the X11 {@code XTEST} extension + * Linux {@link Keyboard} backend: synthesises keystrokes through the X11 {@code XTEST} extension * ({@code XTestFakeKeyEvent}) — the exact mechanism {@link java.awt.Robot} uses on X11 — so the * native image no longer needs the AWT windowing toolkit. * - *

The input string is the cross-platform "{@code modifier+modifier+key}" format parsed by - * {@link com.getpcpanel.commands.KeyMacro}; tokens (AWT {@code VK_}-suffix names) are mapped to X11 - * keysyms, resolved to keycodes via the current keyboard mapping, then pressed/released. + *

The input string is the cross-platform "{@code modifier+modifier+key}" format; tokens (AWT + * {@code VK_}-suffix names) are mapped to X11 keysyms, resolved to keycodes via the current keyboard + * mapping, then pressed/released. * *

Works against an X server (native X11 or XWayland). On a pure Wayland session with no X server * {@code XOpenDisplay} returns null and keystrokes are skipped with a warning. */ @Log4j2 -public final class LinuxKeyboard { +@ApplicationScoped +@LinuxBuild +class LinuxKeyboard implements Keyboard { private interface X11 extends Library { X11 INSTANCE = Native.load("X11", X11.class); @@ -75,10 +80,8 @@ private interface XTest extends Library { private static byte shiftKeycode; private static byte altGrKeycode; - private LinuxKeyboard() { - } - - public static void executeKeyStroke(String input) { + @Override + public void executeKeyStroke(String input) { if (input == null || input.contains("UNDEFINED")) { return; } @@ -131,9 +134,11 @@ public static void executeKeyStroke(String input) { * *

On a pure Wayland session with no X server there is nothing to inject into, so this falls back * to controlling the active player directly through MPRIS on the session D-Bus - * ({@link LinuxMprisMediaControl}). + * ({@link LinuxMprisMediaControl}). The {@code spotify} flag is irrelevant on Linux — the desktop + * already routes the global media key to whichever player is active (Spotify included via MPRIS). */ - public static void sendMediaKey(VolumeButton button) { + @Override + public void sendMediaKey(VolumeButton button, boolean spotify) { var keysym = mediaKeysym(button); synchronized (LOCK) { var disp = display(); @@ -162,7 +167,8 @@ public static void sendMediaKey(VolumeButton button) { * not present on the current layout fall back to the {@code xdotool}-style spare-keycode remap, which * still works on a plain X server (but generally not through the Wayland bridge). */ - public static void typeText(String input) { + @Override + public void typeText(String input) { if (input == null || input.isEmpty()) { return; } diff --git a/src/main/java/com/getpcpanel/cpp/linux/LinuxMprisMediaControl.java b/src/main/java/com/getpcpanel/integration/keyboard/platform/linux/LinuxMprisMediaControl.java similarity index 97% rename from src/main/java/com/getpcpanel/cpp/linux/LinuxMprisMediaControl.java rename to src/main/java/com/getpcpanel/integration/keyboard/platform/linux/LinuxMprisMediaControl.java index 30ca9f83..8babac20 100644 --- a/src/main/java/com/getpcpanel/cpp/linux/LinuxMprisMediaControl.java +++ b/src/main/java/com/getpcpanel/integration/keyboard/platform/linux/LinuxMprisMediaControl.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.linux; +package com.getpcpanel.integration.keyboard.platform.linux; import java.nio.file.Files; import java.nio.file.Path; @@ -12,7 +12,7 @@ import org.freedesktop.dbus.interfaces.DBus; import org.freedesktop.dbus.interfaces.Properties; -import com.getpcpanel.commands.command.CommandMedia.VolumeButton; +import com.getpcpanel.integration.keyboard.command.CommandMedia.VolumeButton; import lombok.extern.log4j.Log4j2; diff --git a/src/main/java/com/getpcpanel/cpp/linux/MprisPlayer.java b/src/main/java/com/getpcpanel/integration/keyboard/platform/linux/MprisPlayer.java similarity index 92% rename from src/main/java/com/getpcpanel/cpp/linux/MprisPlayer.java rename to src/main/java/com/getpcpanel/integration/keyboard/platform/linux/MprisPlayer.java index 21469b81..8d1fb5f9 100644 --- a/src/main/java/com/getpcpanel/cpp/linux/MprisPlayer.java +++ b/src/main/java/com/getpcpanel/integration/keyboard/platform/linux/MprisPlayer.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.linux; +package com.getpcpanel.integration.keyboard.platform.linux; import org.freedesktop.dbus.annotations.DBusInterfaceName; import org.freedesktop.dbus.interfaces.DBusInterface; diff --git a/src/main/java/com/getpcpanel/cpp/osx/OsxKeyboard.java b/src/main/java/com/getpcpanel/integration/keyboard/platform/osx/OsxKeyboard.java similarity index 82% rename from src/main/java/com/getpcpanel/cpp/osx/OsxKeyboard.java rename to src/main/java/com/getpcpanel/integration/keyboard/platform/osx/OsxKeyboard.java index c10bb8f8..f890c275 100644 --- a/src/main/java/com/getpcpanel/cpp/osx/OsxKeyboard.java +++ b/src/main/java/com/getpcpanel/integration/keyboard/platform/osx/OsxKeyboard.java @@ -1,18 +1,27 @@ -package com.getpcpanel.cpp.osx; +package com.getpcpanel.integration.keyboard.platform.osx; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import com.getpcpanel.integration.keyboard.Keyboard; +import com.getpcpanel.integration.keyboard.command.CommandMedia.VolumeButton; +import com.getpcpanel.platform.MacBuild; +import com.getpcpanel.util.os.OsxPermissionHelper; import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.NativeLong; import com.sun.jna.Pointer; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; import lombok.extern.log4j.Log4j2; /** - * Synthesises keystrokes on macOS through CoreGraphics {@code CGEvent}s, replacing {@link java.awt.Robot} - * (AWT is unavailable in the macOS GraalVM native image). + * macOS {@link Keyboard} backend: synthesises keystrokes through CoreGraphics {@code CGEvent}s, + * replacing {@link java.awt.Robot} (AWT is unavailable in the macOS GraalVM native image). Media keys + * delegate to {@link OsxMediaControl} (AppleScript), since macOS has no media-key API safely reachable + * from Java. * *

Architecture independent: every native handle is passed as an opaque {@link Pointer} and every * scalar uses a fixed C width (CGKeyCode = uint16, CGEventTapLocation = uint32, CGEventFlags = uint64), @@ -21,12 +30,16 @@ * *

Requires the Accessibility permission (System Settings > Privacy & Security > Accessibility); * without it macOS silently drops the synthesised events. See - * {@link com.getpcpanel.util.OsxPermissionHelper#isAccessibilityGranted()}. + * {@link com.getpcpanel.util.os.OsxPermissionHelper#isAccessibilityGranted()}. */ @Log4j2 -public final class OsxKeyboard { - private OsxKeyboard() { - } +@ApplicationScoped +@MacBuild +class OsxKeyboard implements Keyboard { + private final AtomicBoolean accessibilityWarned = new AtomicBoolean(); + + @Inject + OsxMediaControl mediaControl; private interface CoreGraphics extends Library { CoreGraphics INSTANCE = Native.load("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics", CoreGraphics.class); @@ -65,14 +78,16 @@ private interface CoreFoundation extends Library { private static final Map KEY_CODES = buildKeyCodes(); /** - * Executes a "{@code modifier+modifier+key}" combination (the same string format the cross-platform - * {@link com.getpcpanel.commands.KeyMacro} parses) by posting a key-down then key-up CGEvent with the - * modifiers folded into the event flags. Unknown keys are logged and skipped rather than throwing. + * Executes a "{@code modifier+modifier+key}" combination by posting a key-down then key-up CGEvent + * with the modifiers folded into the event flags. Unknown keys are logged and skipped rather than + * throwing. */ - public static void executeKeyStroke(String input) { + @Override + public void executeKeyStroke(String input) { if (input == null || input.contains("UNDEFINED")) { return; } + warnIfAccessibilityNotGranted(); var parts = input.replace(" ", "").split("\\+"); long flags = 0; for (var i = 0; i < parts.length - 1; i++) { @@ -100,10 +115,12 @@ public static void executeKeyStroke(String input) { * which is layout-independent. Newlines and tabs are sent as real Return/Tab key codes since the * Unicode-string path does not synthesise them. */ - public static void typeText(String text) { + @Override + public void typeText(String text) { if (text == null || text.isEmpty()) { return; } + warnIfAccessibilityNotGranted(); try { for (var i = 0; i < text.length(); i++) { var c = text.charAt(i); @@ -119,6 +136,17 @@ public static void typeText(String text) { } } + @Override + public void sendMediaKey(VolumeButton button, boolean spotify) { + mediaControl.execute(button, spotify); + } + + private void warnIfAccessibilityNotGranted() { + if (!OsxPermissionHelper.isAccessibilityGranted() && accessibilityWarned.compareAndSet(false, true)) { + log.warn("Keystrokes require Accessibility permission: System Settings > Privacy & Security > Accessibility > enable PCPanel"); + } + } + private static void postChar(char c) { var cg = CoreGraphics.INSTANCE; var chars = new short[] { (short) c }; diff --git a/src/main/java/com/getpcpanel/util/OsxMediaControl.java b/src/main/java/com/getpcpanel/integration/keyboard/platform/osx/OsxMediaControl.java similarity index 91% rename from src/main/java/com/getpcpanel/util/OsxMediaControl.java rename to src/main/java/com/getpcpanel/integration/keyboard/platform/osx/OsxMediaControl.java index 00e5b575..23131e52 100644 --- a/src/main/java/com/getpcpanel/util/OsxMediaControl.java +++ b/src/main/java/com/getpcpanel/integration/keyboard/platform/osx/OsxMediaControl.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.integration.keyboard.platform.osx; import java.io.IOException; import java.nio.charset.Charset; @@ -9,8 +9,9 @@ import org.apache.commons.lang3.StringUtils; -import com.getpcpanel.commands.command.CommandMedia.VolumeButton; +import com.getpcpanel.integration.keyboard.command.CommandMedia.VolumeButton; import com.getpcpanel.platform.MacBuild; +import com.getpcpanel.util.os.ProcessHelper; import jakarta.enterprise.context.ApplicationScoped; import lombok.RequiredArgsConstructor; @@ -23,12 +24,12 @@ @ApplicationScoped @MacBuild @RequiredArgsConstructor -public class OsxMediaControl { +class OsxMediaControl { private static final AtomicBoolean warnedFailure = new AtomicBoolean(); private final ProcessHelper processHelper; private final Set warnedUnsupported = ConcurrentHashMap.newKeySet(); - public void execute(VolumeButton button, boolean spotify) { + void execute(VolumeButton button, boolean spotify) { var verb = switch (button) { case playPause -> "playpause"; case next -> "next track"; diff --git a/src/main/java/com/getpcpanel/cpp/windows/WindowsKeyboard.java b/src/main/java/com/getpcpanel/integration/keyboard/platform/windows/WindowsKeyboard.java similarity index 54% rename from src/main/java/com/getpcpanel/cpp/windows/WindowsKeyboard.java rename to src/main/java/com/getpcpanel/integration/keyboard/platform/windows/WindowsKeyboard.java index 3c16be47..4cfcea5e 100644 --- a/src/main/java/com/getpcpanel/cpp/windows/WindowsKeyboard.java +++ b/src/main/java/com/getpcpanel/integration/keyboard/platform/windows/WindowsKeyboard.java @@ -1,43 +1,60 @@ -package com.getpcpanel.cpp.windows; +package com.getpcpanel.integration.keyboard.platform.windows; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import org.apache.commons.lang3.StringUtils; + +import com.getpcpanel.integration.keyboard.Keyboard; +import com.getpcpanel.integration.keyboard.command.CommandMedia.VolumeButton; +import com.getpcpanel.integration.volume.platform.ISndCtrl; +import com.getpcpanel.integration.volume.platform.windows.SndCtrlWindows; +import com.getpcpanel.platform.WindowsBuild; import com.sun.jna.platform.win32.BaseTSD; import com.sun.jna.platform.win32.User32; import com.sun.jna.platform.win32.WinDef; import com.sun.jna.platform.win32.WinUser; +import com.sun.jna.ptr.IntByReference; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; import lombok.extern.log4j.Log4j2; +import one.util.streamex.StreamEx; /** - * Synthesises keystrokes on Windows through {@code User32.SendInput} (the same Win32 path - * {@link com.getpcpanel.commands.command.CommandMedia} uses for media keys), replacing - * {@link java.awt.Robot} so the native image never needs the AWT windowing toolkit. + * Windows {@link Keyboard} backend: synthesises keystrokes and media keys through + * {@code User32.SendInput}, replacing {@link java.awt.Robot} so the native image never needs the AWT + * windowing toolkit. * - *

The input string is the cross-platform "{@code modifier+modifier+key}" format parsed by - * {@link com.getpcpanel.commands.KeyMacro}; tokens are the AWT {@code VK_}-suffix names. They are - * mapped here to Win32 virtual-key codes and posted as keydown/keyup {@code KEYBDINPUT} events. + *

The keystroke input is the cross-platform "{@code modifier+modifier+key}" format; tokens are the + * AWT {@code VK_}-suffix names, mapped here to Win32 virtual-key codes and posted as keydown/keyup + * {@code KEYBDINPUT} events. Media keys post the global multimedia virtual key, except Spotify-targeted + * actions which locate the Spotify window and send it a {@code WM_APPCOMMAND}. */ @Log4j2 -public final class WindowsKeyboard { +@ApplicationScoped +@WindowsBuild +class WindowsKeyboard implements Keyboard { private static final int KEYEVENTF_KEYUP = 0x0002; private static final int KEYEVENTF_UNICODE = 0x0004; private static final int VK_RETURN = 0x0D; private static final int VK_TAB = 0x09; + private static final int WM_APPCOMMAND = 0x0319; /** AWT-VK-style token (the part after {@code VK_}) → Win32 virtual-key code, for non-trivial keys. */ private static final Map KEY_CODES = buildKeyCodes(); - private WindowsKeyboard() { - } + @Inject + SndCtrlWindows sndCtrl; - public static void executeKeyStroke(String input) { + @Override + public void executeKeyStroke(String input) { if (input == null || input.contains("UNDEFINED")) { return; } var parts = input.replace(" ", "").split("\\+"); - var pressed = new java.util.ArrayList(); + var pressed = new ArrayList(); try { for (var i = 0; i < parts.length - 1; i++) { var vk = modifierVk(parts[i]); @@ -75,7 +92,8 @@ public static void executeKeyStroke(String input) { * astral-plane characters are delivered as their two code units in order). Newlines and tabs are * sent as real {@code VK_RETURN}/{@code VK_TAB} presses since the Unicode path does not produce them. */ - public static void typeText(String text) { + @Override + public void typeText(String text) { if (text == null || text.isEmpty()) { return; } @@ -94,6 +112,60 @@ public static void typeText(String text) { } } + @Override + public void sendMediaKey(VolumeButton button, boolean spotify) { + if (spotify) { + sendSpotifyAppCommand(button); + } else { + sendGlobalMediaKey(button); + } + } + + /** Posts the global multimedia virtual key (routed by Windows to the foreground media session). */ + private void sendGlobalMediaKey(VolumeButton button) { + var input = new WinUser.INPUT(); + input.type = new WinDef.DWORD(WinUser.INPUT.INPUT_KEYBOARD); + input.input.setType("ki"); + input.input.ki.wScan = new WinDef.WORD(0); + input.input.ki.time = new WinDef.DWORD(0); + input.input.ki.dwExtraInfo = new BaseTSD.ULONG_PTR(0); + input.input.ki.wVk = new WinDef.WORD(mediaVk(button)); + input.input.ki.dwFlags = new WinDef.DWORD(0); // keydown + User32.INSTANCE.SendInput(new WinDef.DWORD(1), (WinUser.INPUT[]) input.toArray(1), input.size()); + input.input.ki.dwFlags = new WinDef.DWORD(KEYEVENTF_KEYUP); + User32.INSTANCE.SendInput(new WinDef.DWORD(1), (WinUser.INPUT[]) input.toArray(1), input.size()); + } + + /** Sends the media action straight to the Spotify window via {@code WM_APPCOMMAND}. */ + private void sendSpotifyAppCommand(VolumeButton button) { + var spotifyWnd = findSpotify(); + if (spotifyWnd == null) { + log.warn("Spotify media command: no Spotify window found, nothing sent"); + return; + } + User32.INSTANCE.SendMessage(spotifyWnd, WM_APPCOMMAND, new WinDef.WPARAM(0), new WinDef.LPARAM(appCommand(button))); + } + + private WinDef.HWND findSpotify() { + var result = new WinDef.HWND[] { null }; + var apps = sndCtrl.getRunningApplications(); + var pidIsSpotify = StreamEx.of(apps).mapToEntry(ISndCtrl.RunningApplication::pid, ra -> StringUtils.equalsIgnoreCase("spotify.exe", ra.file().getName())).distinctKeys().toMap(); + var spotifyPids = pidIsSpotify.entrySet().stream().filter(Map.Entry::getValue).map(Map.Entry::getKey).toList(); + var windowsSeen = new int[] { 0 }; + User32.INSTANCE.EnumWindows((hWnd, data) -> { + windowsSeen[0]++; + var target = new IntByReference(); + User32.INSTANCE.GetWindowThreadProcessId(hWnd, target); + if (pidIsSpotify.getOrDefault(target.getValue(), false)) { + result[0] = hWnd; + return false; + } + return true; + }, null); + log.debug("findSpotify: {} running apps, spotify pids={}, windows enumerated={}, match={}", apps.size(), spotifyPids, windowsSeen[0], result[0]); + return result[0]; + } + private static void sendUnicode(char c, boolean keyUp) { var input = new WinUser.INPUT(); input.type = new WinDef.DWORD(WinUser.INPUT.INPUT_KEYBOARD); @@ -118,6 +190,28 @@ private static void sendVk(int vk, boolean keyUp) { User32.INSTANCE.SendInput(new WinDef.DWORD(1), (WinUser.INPUT[]) input.toArray(1), input.size()); } + /** Global multimedia virtual-key code (VK_MEDIA_... / VK_VOLUME_MUTE) for a media button. */ + private static int mediaVk(VolumeButton button) { + return switch (button) { + case mute -> 0xAD; // VK_VOLUME_MUTE + case next -> 0xB0; // VK_MEDIA_NEXT_TRACK + case prev -> 0xB1; // VK_MEDIA_PREV_TRACK + case stop -> 0xB2; // VK_MEDIA_STOP + case playPause -> 0xB3; // VK_MEDIA_PLAY_PAUSE + }; + } + + /** {@code APPCOMMAND_*} lParam (shifted into the high word) for a Spotify {@code WM_APPCOMMAND}. */ + private static int appCommand(VolumeButton button) { + return switch (button) { + case mute -> 0x80000; // APPCOMMAND_VOLUME_MUTE + case next -> 0xB0000; // APPCOMMAND_MEDIA_NEXTTRACK + case prev -> 0xC0000; // APPCOMMAND_MEDIA_PREVIOUSTRACK + case stop -> 0xD0000; // APPCOMMAND_MEDIA_STOP + case playPause -> 0xE0000; // APPCOMMAND_MEDIA_PLAY_PAUSE + }; + } + static int modifierVk(String mod) { return switch (mod) { case "ctrl" -> 0x11; // VK_CONTROL diff --git a/src/main/java/com/getpcpanel/mqtt/MqttDeviceColorService.java b/src/main/java/com/getpcpanel/integration/mqtt/MqttDeviceColorService.java similarity index 94% rename from src/main/java/com/getpcpanel/mqtt/MqttDeviceColorService.java rename to src/main/java/com/getpcpanel/integration/mqtt/MqttDeviceColorService.java index a0cb1a10..aa0ea6c9 100644 --- a/src/main/java/com/getpcpanel/mqtt/MqttDeviceColorService.java +++ b/src/main/java/com/getpcpanel/integration/mqtt/MqttDeviceColorService.java @@ -1,10 +1,10 @@ -package com.getpcpanel.mqtt; +package com.getpcpanel.integration.mqtt; -import static com.getpcpanel.mqtt.MqttTopicHelper.ColorType.dial; -import static com.getpcpanel.mqtt.MqttTopicHelper.ColorType.label; -import static com.getpcpanel.mqtt.MqttTopicHelper.ColorType.logo; -import static com.getpcpanel.mqtt.MqttTopicHelper.ColorType.slider; -import static com.getpcpanel.mqtt.MqttTopicHelper.ValueType.brightness; +import static com.getpcpanel.integration.mqtt.MqttTopicHelper.ColorType.dial; +import static com.getpcpanel.integration.mqtt.MqttTopicHelper.ColorType.label; +import static com.getpcpanel.integration.mqtt.MqttTopicHelper.ColorType.logo; +import static com.getpcpanel.integration.mqtt.MqttTopicHelper.ColorType.slider; +import static com.getpcpanel.integration.mqtt.MqttTopicHelper.ValueType.brightness; import static com.getpcpanel.util.Util.parseColor; import static com.getpcpanel.util.Util.parseColorComponents; @@ -20,8 +20,8 @@ import com.getpcpanel.device.Device; import com.getpcpanel.device.GlobalBrightnessChangedEvent; -import com.getpcpanel.mqtt.MqttTopicHelper.ColorType; -import com.getpcpanel.mqtt.MqttTopicHelper.DeviceMqttTopicHelper; +import com.getpcpanel.integration.mqtt.MqttTopicHelper.ColorType; +import com.getpcpanel.integration.mqtt.MqttTopicHelper.DeviceMqttTopicHelper; import com.getpcpanel.profile.dto.LightingConfig; import com.getpcpanel.profile.dto.SingleKnobLightingConfig; import com.getpcpanel.profile.dto.SingleKnobLightingConfig.SINGLE_KNOB_MODE; @@ -46,7 +46,7 @@ @Log4j2 @ApplicationScoped @Priority(1) -public class MqttDeviceColorService implements IOverrideColorProviderProvider { +class MqttDeviceColorService implements IOverrideColorProviderProvider { @FunctionalInterface interface TriFunction { diff --git a/src/main/java/com/getpcpanel/mqtt/MqttDeviceService.java b/src/main/java/com/getpcpanel/integration/mqtt/MqttDeviceService.java similarity index 84% rename from src/main/java/com/getpcpanel/mqtt/MqttDeviceService.java rename to src/main/java/com/getpcpanel/integration/mqtt/MqttDeviceService.java index ef69fc44..81389f5d 100644 --- a/src/main/java/com/getpcpanel/mqtt/MqttDeviceService.java +++ b/src/main/java/com/getpcpanel/integration/mqtt/MqttDeviceService.java @@ -1,13 +1,13 @@ -package com.getpcpanel.mqtt; +package com.getpcpanel.integration.mqtt; -import static com.getpcpanel.mqtt.MqttService.ORDER_OF_SAVE; -import static com.getpcpanel.mqtt.MqttTopicHelper.ActionType.button; -import static com.getpcpanel.mqtt.MqttTopicHelper.ColorType.dial; -import static com.getpcpanel.mqtt.MqttTopicHelper.ColorType.label; -import static com.getpcpanel.mqtt.MqttTopicHelper.ColorType.logo; -import static com.getpcpanel.mqtt.MqttTopicHelper.ColorType.slider; -import static com.getpcpanel.mqtt.MqttTopicHelper.ValueType.analog; -import static com.getpcpanel.mqtt.MqttTopicHelper.ValueType.brightness; +import static com.getpcpanel.integration.mqtt.MqttService.ORDER_OF_SAVE; +import static com.getpcpanel.integration.mqtt.MqttTopicHelper.ActionType.button; +import static com.getpcpanel.integration.mqtt.MqttTopicHelper.ColorType.dial; +import static com.getpcpanel.integration.mqtt.MqttTopicHelper.ColorType.label; +import static com.getpcpanel.integration.mqtt.MqttTopicHelper.ColorType.logo; +import static com.getpcpanel.integration.mqtt.MqttTopicHelper.ColorType.slider; +import static com.getpcpanel.integration.mqtt.MqttTopicHelper.ValueType.analog; +import static com.getpcpanel.integration.mqtt.MqttTopicHelper.ValueType.brightness; import java.util.HashSet; import java.util.Set; @@ -18,13 +18,13 @@ import com.getpcpanel.device.Device; import com.getpcpanel.device.GlobalBrightnessChangedEvent; -import com.getpcpanel.hid.ButtonClickEvent; -import com.getpcpanel.hid.DeviceCommunicationHandler.ButtonPressEvent; -import com.getpcpanel.hid.DeviceCommunicationHandler.KnobRotateEvent; -import com.getpcpanel.hid.DeviceHolder; -import com.getpcpanel.hid.DeviceHolder.DeviceFullyConnectedEvent; -import com.getpcpanel.mqtt.MqttTopicHelper.ColorType; -import com.getpcpanel.mqtt.MqttTopicHelper.DeviceMqttTopicHelper; +import com.getpcpanel.device.provider.pcpanel.ButtonClickEvent; +import com.getpcpanel.device.provider.pcpanel.DeviceCommunicationHandler.ButtonPressEvent; +import com.getpcpanel.device.provider.pcpanel.DeviceCommunicationHandler.KnobRotateEvent; +import com.getpcpanel.device.DeviceHolder; +import com.getpcpanel.device.DeviceHolder.DeviceFullyConnectedEvent; +import com.getpcpanel.integration.mqtt.MqttTopicHelper.ColorType; +import com.getpcpanel.integration.mqtt.MqttTopicHelper.DeviceMqttTopicHelper; import com.getpcpanel.profile.SaveService; import com.getpcpanel.profile.SaveService.SaveEvent; import com.getpcpanel.profile.dto.LightingConfig; @@ -192,6 +192,6 @@ private void writeButtons(Device device) { } } - record MqttEvent(String event_type) { + public record MqttEvent(String event_type) { } } diff --git a/src/main/java/com/getpcpanel/mqtt/MqttHomeAssistantHelper.java b/src/main/java/com/getpcpanel/integration/mqtt/MqttHomeAssistantHelper.java similarity index 94% rename from src/main/java/com/getpcpanel/mqtt/MqttHomeAssistantHelper.java rename to src/main/java/com/getpcpanel/integration/mqtt/MqttHomeAssistantHelper.java index 0ed6ea1d..2cb57360 100644 --- a/src/main/java/com/getpcpanel/mqtt/MqttHomeAssistantHelper.java +++ b/src/main/java/com/getpcpanel/integration/mqtt/MqttHomeAssistantHelper.java @@ -1,7 +1,7 @@ -package com.getpcpanel.mqtt; +package com.getpcpanel.integration.mqtt; -import static com.getpcpanel.mqtt.MqttDeviceColorService.EFFECT_NONE; -import static com.getpcpanel.mqtt.MqttDeviceColorService.EFFECT_STOP_OVERRIDE; +import static com.getpcpanel.integration.mqtt.MqttDeviceColorService.EFFECT_NONE; +import static com.getpcpanel.integration.mqtt.MqttDeviceColorService.EFFECT_STOP_OVERRIDE; import java.util.List; @@ -13,10 +13,10 @@ import javax.annotation.Nullable; import com.getpcpanel.device.Device; -import com.getpcpanel.mqtt.MqttTopicHelper.ActionType; -import com.getpcpanel.mqtt.MqttTopicHelper.ColorType; -import com.getpcpanel.mqtt.MqttTopicHelper.ValueType; -import com.getpcpanel.profile.dto.MqttSettings; +import com.getpcpanel.integration.mqtt.MqttTopicHelper.ActionType; +import com.getpcpanel.integration.mqtt.MqttTopicHelper.ColorType; +import com.getpcpanel.integration.mqtt.MqttTopicHelper.ValueType; +import com.getpcpanel.integration.mqtt.dto.MqttSettings; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; @@ -213,7 +213,7 @@ private String determineAnalogName(Device device, int i) { return "Slider " + (i - buttonCount + 1); } - record HomeAssistantNumberConfig( + public record HomeAssistantNumberConfig( HomeAssistantDevice device, @Nullable HomeAssistantAvailability availability, String name, @@ -244,7 +244,7 @@ record HomeAssistantNumberConfig( } } - record HomeAssistantLightConfig( + public record HomeAssistantLightConfig( HomeAssistantDevice device, @Nullable HomeAssistantAvailability availability, String name, @@ -282,7 +282,7 @@ record HomeAssistantLightConfig( } } - record HomeAssistantButtonConfig( // Is actually a binary sensor + public record HomeAssistantButtonConfig( // Is actually a binary sensor HomeAssistantDevice device, @Nullable HomeAssistantAvailability availability, String state_topic, @@ -304,7 +304,7 @@ record HomeAssistantButtonConfig( // Is actually a binary sensor } } - record HomeAssistantButtonEventConfig(HomeAssistantDevice device, + public record HomeAssistantButtonEventConfig(HomeAssistantDevice device, @Nullable HomeAssistantAvailability availability, String state_topic, String name, @@ -350,7 +350,7 @@ private HomeAssistantAvailability buildAvailability(MqttSettings settings) { return null; } - record HomeAssistantDevice( + public record HomeAssistantDevice( String sw_version, List identifiers, String manufacturer, @@ -361,7 +361,7 @@ record HomeAssistantDevice( ) { } - record HomeAssistantAvailability( + public record HomeAssistantAvailability( String topic, String payload_available, String payload_not_available, diff --git a/src/main/java/com/getpcpanel/mqtt/MqttService.java b/src/main/java/com/getpcpanel/integration/mqtt/MqttService.java similarity index 98% rename from src/main/java/com/getpcpanel/mqtt/MqttService.java rename to src/main/java/com/getpcpanel/integration/mqtt/MqttService.java index 707dded2..de0dddd9 100644 --- a/src/main/java/com/getpcpanel/mqtt/MqttService.java +++ b/src/main/java/com/getpcpanel/integration/mqtt/MqttService.java @@ -1,4 +1,4 @@ -package com.getpcpanel.mqtt; +package com.getpcpanel.integration.mqtt; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -28,8 +28,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.getpcpanel.profile.SaveService.SaveEvent; -import com.getpcpanel.profile.dto.MqttSettings; -import com.getpcpanel.util.Debouncer; +import com.getpcpanel.integration.mqtt.dto.MqttSettings; +import com.getpcpanel.util.concurrent.Debouncer; import jakarta.annotation.Priority; import jakarta.enterprise.context.ApplicationScoped; diff --git a/src/main/java/com/getpcpanel/mqtt/MqttStatusEvent.java b/src/main/java/com/getpcpanel/integration/mqtt/MqttStatusEvent.java similarity index 56% rename from src/main/java/com/getpcpanel/mqtt/MqttStatusEvent.java rename to src/main/java/com/getpcpanel/integration/mqtt/MqttStatusEvent.java index e46bf34b..fe4b2542 100644 --- a/src/main/java/com/getpcpanel/mqtt/MqttStatusEvent.java +++ b/src/main/java/com/getpcpanel/integration/mqtt/MqttStatusEvent.java @@ -1,4 +1,4 @@ -package com.getpcpanel.mqtt; +package com.getpcpanel.integration.mqtt; public record MqttStatusEvent(boolean connected) { } diff --git a/src/main/java/com/getpcpanel/mqtt/MqttTopicHelper.java b/src/main/java/com/getpcpanel/integration/mqtt/MqttTopicHelper.java similarity index 96% rename from src/main/java/com/getpcpanel/mqtt/MqttTopicHelper.java rename to src/main/java/com/getpcpanel/integration/mqtt/MqttTopicHelper.java index 14f8e0fb..a3ec9368 100644 --- a/src/main/java/com/getpcpanel/mqtt/MqttTopicHelper.java +++ b/src/main/java/com/getpcpanel/integration/mqtt/MqttTopicHelper.java @@ -1,7 +1,7 @@ -package com.getpcpanel.mqtt; +package com.getpcpanel.integration.mqtt; import com.getpcpanel.profile.SaveService; -import com.getpcpanel.profile.dto.MqttSettings; +import com.getpcpanel.integration.mqtt.dto.MqttSettings; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandMqttPublish.java b/src/main/java/com/getpcpanel/integration/mqtt/command/CommandMqttPublish.java similarity index 74% rename from src/main/java/com/getpcpanel/commands/command/CommandMqttPublish.java rename to src/main/java/com/getpcpanel/integration/mqtt/command/CommandMqttPublish.java index 251df67a..352c9f62 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandMqttPublish.java +++ b/src/main/java/com/getpcpanel/integration/mqtt/command/CommandMqttPublish.java @@ -1,12 +1,17 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.mqtt.command; import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; -import com.getpcpanel.mqtt.MqttService; +import com.getpcpanel.integration.mqtt.MqttService; +import com.getpcpanel.commands.command.CommandValueOutput; import com.getpcpanel.util.CdiHelper; import com.getpcpanel.util.ValueInterpolator; @@ -20,6 +25,8 @@ */ @Getter @ToString(callSuper = true) +@JsonTypeName("mqtt.publish") +@CommandMeta(label = "MQTT publish", category = CommandCategory.system, kinds = {CommandKind.dial, CommandKind.button}, icon = "zap", legacyIds = {"com.getpcpanel.commands.command.CommandMqttPublish"}) public class CommandMqttPublish extends CommandValueOutput { private final String topic; private final String payload; diff --git a/src/main/java/com/getpcpanel/integration/mqtt/command/MqttCommandModule.java b/src/main/java/com/getpcpanel/integration/mqtt/command/MqttCommandModule.java new file mode 100644 index 00000000..9875e422 --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/mqtt/command/MqttCommandModule.java @@ -0,0 +1,21 @@ +package com.getpcpanel.integration.mqtt.command; + +import java.util.List; + +import com.getpcpanel.commands.CommandModule; +import com.getpcpanel.commands.command.Command; + +import jakarta.enterprise.context.ApplicationScoped; + +/** + * Mqtt feature module: registers its own command types via the + * {@link com.getpcpanel.commands.CommandModule} SPI. Adding/removing a command touches only this package. + */ +@ApplicationScoped +public class MqttCommandModule implements CommandModule { + @Override + public List> commandTypes() { + return List.of( + CommandMqttPublish.class); + } +} diff --git a/src/main/java/com/getpcpanel/profile/dto/MqttSettings.java b/src/main/java/com/getpcpanel/integration/mqtt/dto/MqttSettings.java similarity index 95% rename from src/main/java/com/getpcpanel/profile/dto/MqttSettings.java rename to src/main/java/com/getpcpanel/integration/mqtt/dto/MqttSettings.java index 9284dd70..2d9fd742 100644 --- a/src/main/java/com/getpcpanel/profile/dto/MqttSettings.java +++ b/src/main/java/com/getpcpanel/integration/mqtt/dto/MqttSettings.java @@ -1,4 +1,4 @@ -package com.getpcpanel.profile.dto; +package com.getpcpanel.integration.mqtt.dto; public record MqttSettings(boolean enabled, String host, Integer port, String username, String password, boolean secure, String baseTopic, diff --git a/src/main/java/com/getpcpanel/obs/OBS.java b/src/main/java/com/getpcpanel/integration/obs/OBS.java similarity index 99% rename from src/main/java/com/getpcpanel/obs/OBS.java rename to src/main/java/com/getpcpanel/integration/obs/OBS.java index 4deea1af..b02b1ab3 100644 --- a/src/main/java/com/getpcpanel/obs/OBS.java +++ b/src/main/java/com/getpcpanel/integration/obs/OBS.java @@ -1,4 +1,4 @@ -package com.getpcpanel.obs; +package com.getpcpanel.integration.obs; import java.util.List; import java.util.Map; @@ -6,7 +6,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.getpcpanel.profile.SaveService; import com.getpcpanel.profile.SaveService.SaveEvent; -import com.getpcpanel.util.ReconnectBackoff; +import com.getpcpanel.util.concurrent.ReconnectBackoff; import io.quarkus.scheduler.Scheduled; import jakarta.annotation.PreDestroy; diff --git a/src/main/java/com/getpcpanel/obs/OBSConnectEvent.java b/src/main/java/com/getpcpanel/integration/obs/OBSConnectEvent.java similarity index 57% rename from src/main/java/com/getpcpanel/obs/OBSConnectEvent.java rename to src/main/java/com/getpcpanel/integration/obs/OBSConnectEvent.java index aba51aeb..51485a18 100644 --- a/src/main/java/com/getpcpanel/obs/OBSConnectEvent.java +++ b/src/main/java/com/getpcpanel/integration/obs/OBSConnectEvent.java @@ -1,4 +1,4 @@ -package com.getpcpanel.obs; +package com.getpcpanel.integration.obs; public record OBSConnectEvent(boolean connected) { } diff --git a/src/main/java/com/getpcpanel/obs/OBSMuteEvent.java b/src/main/java/com/getpcpanel/integration/obs/OBSMuteEvent.java similarity index 60% rename from src/main/java/com/getpcpanel/obs/OBSMuteEvent.java rename to src/main/java/com/getpcpanel/integration/obs/OBSMuteEvent.java index 6b23ff0c..d7a51de6 100644 --- a/src/main/java/com/getpcpanel/obs/OBSMuteEvent.java +++ b/src/main/java/com/getpcpanel/integration/obs/OBSMuteEvent.java @@ -1,4 +1,4 @@ -package com.getpcpanel.obs; +package com.getpcpanel.integration.obs; public record OBSMuteEvent(String input, boolean muted) { } diff --git a/src/main/java/com/getpcpanel/obs/ObsConnectedVolumeService.java b/src/main/java/com/getpcpanel/integration/obs/ObsConnectedVolumeService.java similarity index 72% rename from src/main/java/com/getpcpanel/obs/ObsConnectedVolumeService.java rename to src/main/java/com/getpcpanel/integration/obs/ObsConnectedVolumeService.java index 4570c183..0e167745 100644 --- a/src/main/java/com/getpcpanel/obs/ObsConnectedVolumeService.java +++ b/src/main/java/com/getpcpanel/integration/obs/ObsConnectedVolumeService.java @@ -1,8 +1,8 @@ -package com.getpcpanel.obs; +package com.getpcpanel.integration.obs; import java.util.function.Function; -import com.getpcpanel.commands.command.CommandObsSetSourceVolume; -import com.getpcpanel.hid.DeviceHolder; +import com.getpcpanel.integration.obs.command.CommandObsSetSourceVolume; +import com.getpcpanel.device.DeviceHolder; import com.getpcpanel.platform.WindowsBuild; import jakarta.enterprise.context.ApplicationScoped; @@ -11,7 +11,7 @@ @ApplicationScoped @WindowsBuild -public class ObsConnectedVolumeService { +class ObsConnectedVolumeService { @Inject DeviceHolder devices; public void onVoiceMeeterConnected(@Observes OBSConnectEvent event) { diff --git a/src/main/java/com/getpcpanel/integration/obs/ObsIconHandler.java b/src/main/java/com/getpcpanel/integration/obs/ObsIconHandler.java new file mode 100644 index 00000000..10b9dd2f --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/obs/ObsIconHandler.java @@ -0,0 +1,28 @@ +package com.getpcpanel.integration.obs; + +import java.awt.image.BufferedImage; +import java.util.Optional; + +import com.getpcpanel.commands.IIconHandler; +import com.getpcpanel.commands.IconService; +import com.getpcpanel.integration.obs.command.CommandObs; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +/** Supplies the OBS icon for any OBS command, contributed via the {@link IIconHandler} SPI. */ +@ApplicationScoped +class ObsIconHandler implements IIconHandler { + @Inject + IconService iconService; + + @Override + public Class getCommandClass() { + return CommandObs.class; + } + + @Override + public Optional supplyImage(CommandObs cmd) { + return Optional.ofNullable(iconService.OBS); + } +} diff --git a/src/main/java/com/getpcpanel/mutecolor/ObsMuteResolver.java b/src/main/java/com/getpcpanel/integration/obs/ObsMuteResolver.java similarity index 81% rename from src/main/java/com/getpcpanel/mutecolor/ObsMuteResolver.java rename to src/main/java/com/getpcpanel/integration/obs/ObsMuteResolver.java index cba2dde3..8d2a52ff 100644 --- a/src/main/java/com/getpcpanel/mutecolor/ObsMuteResolver.java +++ b/src/main/java/com/getpcpanel/integration/obs/ObsMuteResolver.java @@ -1,13 +1,14 @@ -package com.getpcpanel.mutecolor; +package com.getpcpanel.integration.obs; +import com.getpcpanel.integration.volume.mutecolor.MuteStateResolver; import java.util.Optional; import org.apache.commons.lang3.StringUtils; import com.getpcpanel.commands.Commands; -import com.getpcpanel.commands.command.CommandObsMuteSource; -import com.getpcpanel.commands.command.CommandObsSetSourceVolume; -import com.getpcpanel.obs.OBS; +import com.getpcpanel.integration.obs.command.CommandObsMuteSource; +import com.getpcpanel.integration.obs.command.CommandObsSetSourceVolume; +import com.getpcpanel.integration.obs.OBS; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; @@ -17,7 +18,7 @@ * ({@link CommandObsSetSourceVolume}) or an OBS mute-source button ({@link CommandObsMuteSource}). */ @ApplicationScoped -public class ObsMuteResolver implements MuteStateResolver { +class ObsMuteResolver implements MuteStateResolver { @Inject OBS obs; diff --git a/src/main/java/com/getpcpanel/obs/ObsWebSocketClient.java b/src/main/java/com/getpcpanel/integration/obs/ObsWebSocketClient.java similarity index 99% rename from src/main/java/com/getpcpanel/obs/ObsWebSocketClient.java rename to src/main/java/com/getpcpanel/integration/obs/ObsWebSocketClient.java index 4a8e46a5..c7bb5bc2 100644 --- a/src/main/java/com/getpcpanel/obs/ObsWebSocketClient.java +++ b/src/main/java/com/getpcpanel/integration/obs/ObsWebSocketClient.java @@ -1,4 +1,4 @@ -package com.getpcpanel.obs; +package com.getpcpanel.integration.obs; import java.net.URI; import com.getpcpanel.util.SharedHttpClient; @@ -30,7 +30,7 @@ * request/response correlation, InputMuteStateChanged events. */ @Log4j2 -public class ObsWebSocketClient implements WebSocket.Listener { +class ObsWebSocketClient implements WebSocket.Listener { // OBS WebSocket 5 opcodes private static final int OP_HELLO = 0; diff --git a/src/main/java/com/getpcpanel/integration/obs/command/CommandObs.java b/src/main/java/com/getpcpanel/integration/obs/command/CommandObs.java new file mode 100644 index 00000000..8d3536f0 --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/obs/command/CommandObs.java @@ -0,0 +1,9 @@ +package com.getpcpanel.integration.obs.command; + +import com.getpcpanel.commands.command.Command; + +import lombok.Getter; + +@Getter +public abstract class CommandObs extends Command { +} diff --git a/src/main/java/com/getpcpanel/commands/command/CommandObsAction.java b/src/main/java/com/getpcpanel/integration/obs/command/CommandObsAction.java similarity index 78% rename from src/main/java/com/getpcpanel/commands/command/CommandObsAction.java rename to src/main/java/com/getpcpanel/integration/obs/command/CommandObsAction.java index ee125786..3e912ee4 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandObsAction.java +++ b/src/main/java/com/getpcpanel/integration/obs/command/CommandObsAction.java @@ -1,8 +1,13 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.obs.command; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; -import com.getpcpanel.obs.OBS; +import com.getpcpanel.integration.obs.OBS; +import com.getpcpanel.commands.command.ButtonAction; import com.getpcpanel.util.CdiHelper; import lombok.Getter; @@ -14,6 +19,8 @@ */ @Getter @ToString(callSuper = true) +@JsonTypeName("obs.action") +@CommandMeta(label = "OBS — stream / record", category = CommandCategory.integration, kinds = {CommandKind.button}, integration = "obs", icon = "film", legacyIds = {"com.getpcpanel.commands.command.CommandObsAction"}) public class CommandObsAction extends CommandObs implements ButtonAction { private final ObsActionType action; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandObsMuteSource.java b/src/main/java/com/getpcpanel/integration/obs/command/CommandObsMuteSource.java similarity index 61% rename from src/main/java/com/getpcpanel/commands/command/CommandObsMuteSource.java rename to src/main/java/com/getpcpanel/integration/obs/command/CommandObsMuteSource.java index 7bb99479..22f37e1e 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandObsMuteSource.java +++ b/src/main/java/com/getpcpanel/integration/obs/command/CommandObsMuteSource.java @@ -1,9 +1,14 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.obs.command; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; -import com.getpcpanel.cpp.MuteType; -import com.getpcpanel.obs.OBS; +import com.getpcpanel.integration.volume.platform.MuteType; +import com.getpcpanel.integration.obs.OBS; +import com.getpcpanel.commands.command.ButtonAction; import com.getpcpanel.util.CdiHelper; import lombok.Getter; @@ -11,6 +16,8 @@ @Getter @ToString(callSuper = true) +@JsonTypeName("obs.mute-source") +@CommandMeta(label = "OBS — mute source", category = CommandCategory.integration, kinds = {CommandKind.button}, integration = "obs", icon = "mic-off", legacyIds = {"com.getpcpanel.commands.command.CommandObsMuteSource"}) public class CommandObsMuteSource extends CommandObs implements ButtonAction { private final String source; private final MuteType type; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandObsSetScene.java b/src/main/java/com/getpcpanel/integration/obs/command/CommandObsSetScene.java similarity index 55% rename from src/main/java/com/getpcpanel/commands/command/CommandObsSetScene.java rename to src/main/java/com/getpcpanel/integration/obs/command/CommandObsSetScene.java index 047944d7..4fa284f7 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandObsSetScene.java +++ b/src/main/java/com/getpcpanel/integration/obs/command/CommandObsSetScene.java @@ -1,15 +1,22 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.obs.command; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; +import com.getpcpanel.commands.command.ButtonAction; import com.getpcpanel.util.CdiHelper; -import com.getpcpanel.obs.OBS; +import com.getpcpanel.integration.obs.OBS; import lombok.Getter; import lombok.ToString; @Getter @ToString(callSuper = true) +@JsonTypeName("obs.set-scene") +@CommandMeta(label = "OBS — switch scene", category = CommandCategory.integration, kinds = {CommandKind.button}, integration = "obs", icon = "film", legacyIds = {"com.getpcpanel.commands.command.CommandObsSetScene"}) public class CommandObsSetScene extends CommandObs implements ButtonAction { private final String scene; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandObsSetSourceVolume.java b/src/main/java/com/getpcpanel/integration/obs/command/CommandObsSetSourceVolume.java similarity index 62% rename from src/main/java/com/getpcpanel/commands/command/CommandObsSetSourceVolume.java rename to src/main/java/com/getpcpanel/integration/obs/command/CommandObsSetSourceVolume.java index 2cfd1f59..3fd31495 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandObsSetSourceVolume.java +++ b/src/main/java/com/getpcpanel/integration/obs/command/CommandObsSetSourceVolume.java @@ -1,15 +1,22 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.obs.command; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; +import com.getpcpanel.commands.command.DialAction; import com.getpcpanel.util.CdiHelper; -import com.getpcpanel.obs.OBS; +import com.getpcpanel.integration.obs.OBS; import lombok.Getter; import lombok.ToString; @Getter @ToString(callSuper = true) +@JsonTypeName("obs.set-source-volume") +@CommandMeta(label = "OBS — source volume", category = CommandCategory.integration, kinds = {CommandKind.dial}, integration = "obs", icon = "sliders", legacyIds = {"com.getpcpanel.commands.command.CommandObsSetSourceVolume"}) public class CommandObsSetSourceVolume extends CommandObs implements DialAction { private final String sourceName; private final DialCommandParams dialParams; diff --git a/src/main/java/com/getpcpanel/integration/obs/command/ObsCommandModule.java b/src/main/java/com/getpcpanel/integration/obs/command/ObsCommandModule.java new file mode 100644 index 00000000..becc9adf --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/obs/command/ObsCommandModule.java @@ -0,0 +1,24 @@ +package com.getpcpanel.integration.obs.command; + +import java.util.List; + +import com.getpcpanel.commands.CommandModule; +import com.getpcpanel.commands.command.Command; + +import jakarta.enterprise.context.ApplicationScoped; + +/** + * Obs feature module: registers its own command types via the + * {@link com.getpcpanel.commands.CommandModule} SPI. Adding/removing a command touches only this package. + */ +@ApplicationScoped +public class ObsCommandModule implements CommandModule { + @Override + public List> commandTypes() { + return List.of( + CommandObsAction.class, + CommandObsMuteSource.class, + CommandObsSetScene.class, + CommandObsSetSourceVolume.class); + } +} diff --git a/src/main/java/com/getpcpanel/rest/ObsResource.java b/src/main/java/com/getpcpanel/integration/obs/rest/ObsResource.java similarity index 89% rename from src/main/java/com/getpcpanel/rest/ObsResource.java rename to src/main/java/com/getpcpanel/integration/obs/rest/ObsResource.java index 17fd7221..4d510749 100644 --- a/src/main/java/com/getpcpanel/rest/ObsResource.java +++ b/src/main/java/com/getpcpanel/integration/obs/rest/ObsResource.java @@ -1,8 +1,8 @@ -package com.getpcpanel.rest; +package com.getpcpanel.integration.obs.rest; import java.util.List; -import com.getpcpanel.obs.OBS; +import com.getpcpanel.integration.obs.OBS; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; diff --git a/src/main/java/com/getpcpanel/osc/OSCService.java b/src/main/java/com/getpcpanel/integration/osc/OSCService.java similarity index 95% rename from src/main/java/com/getpcpanel/osc/OSCService.java rename to src/main/java/com/getpcpanel/integration/osc/OSCService.java index 823e050e..3b2b370f 100644 --- a/src/main/java/com/getpcpanel/osc/OSCService.java +++ b/src/main/java/com/getpcpanel/integration/osc/OSCService.java @@ -1,4 +1,4 @@ -package com.getpcpanel.osc; +package com.getpcpanel.integration.osc; import java.io.IOException; import java.net.InetAddress; @@ -10,11 +10,11 @@ import javax.annotation.Nonnull; -import com.getpcpanel.hid.DeviceCommunicationHandler.ButtonPressEvent; -import com.getpcpanel.hid.DeviceCommunicationHandler.KnobRotateEvent; +import com.getpcpanel.device.provider.pcpanel.DeviceCommunicationHandler.ButtonPressEvent; +import com.getpcpanel.device.provider.pcpanel.DeviceCommunicationHandler.KnobRotateEvent; import com.getpcpanel.profile.SaveService; -import com.getpcpanel.profile.dto.OSCBinding; -import com.getpcpanel.profile.dto.OSCConnectionInfo; +import com.getpcpanel.integration.osc.dto.OSCBinding; +import com.getpcpanel.integration.osc.dto.OSCConnectionInfo; import com.getpcpanel.util.Util; import com.illposed.osc.OSCBadDataEvent; import com.illposed.osc.OSCBundle; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandOscSend.java b/src/main/java/com/getpcpanel/integration/osc/command/CommandOscSend.java similarity index 71% rename from src/main/java/com/getpcpanel/commands/command/CommandOscSend.java rename to src/main/java/com/getpcpanel/integration/osc/command/CommandOscSend.java index 95d1e65a..b184981b 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandOscSend.java +++ b/src/main/java/com/getpcpanel/integration/osc/command/CommandOscSend.java @@ -1,12 +1,17 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.osc.command; import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; -import com.getpcpanel.osc.OSCService; +import com.getpcpanel.integration.osc.OSCService; +import com.getpcpanel.commands.command.CommandValueOutput; import com.getpcpanel.util.CdiHelper; import lombok.Getter; @@ -19,6 +24,8 @@ */ @Getter @ToString(callSuper = true) +@JsonTypeName("osc.send") +@CommandMeta(label = "OSC send", category = CommandCategory.system, kinds = {CommandKind.dial, CommandKind.button}, icon = "sliders", legacyIds = {"com.getpcpanel.commands.command.CommandOscSend"}) public class CommandOscSend extends CommandValueOutput { private final String address; diff --git a/src/main/java/com/getpcpanel/integration/osc/command/OscCommandModule.java b/src/main/java/com/getpcpanel/integration/osc/command/OscCommandModule.java new file mode 100644 index 00000000..6d40b2a6 --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/osc/command/OscCommandModule.java @@ -0,0 +1,21 @@ +package com.getpcpanel.integration.osc.command; + +import java.util.List; + +import com.getpcpanel.commands.CommandModule; +import com.getpcpanel.commands.command.Command; + +import jakarta.enterprise.context.ApplicationScoped; + +/** + * Osc feature module: registers its own command types via the + * {@link com.getpcpanel.commands.CommandModule} SPI. Adding/removing a command touches only this package. + */ +@ApplicationScoped +public class OscCommandModule implements CommandModule { + @Override + public List> commandTypes() { + return List.of( + CommandOscSend.class); + } +} diff --git a/src/main/java/com/getpcpanel/profile/dto/OSCBinding.java b/src/main/java/com/getpcpanel/integration/osc/dto/OSCBinding.java similarity index 78% rename from src/main/java/com/getpcpanel/profile/dto/OSCBinding.java rename to src/main/java/com/getpcpanel/integration/osc/dto/OSCBinding.java index 6b0c4b69..4b0bac43 100644 --- a/src/main/java/com/getpcpanel/profile/dto/OSCBinding.java +++ b/src/main/java/com/getpcpanel/integration/osc/dto/OSCBinding.java @@ -1,4 +1,4 @@ -package com.getpcpanel.profile.dto; +package com.getpcpanel.integration.osc.dto; public record OSCBinding(String address, float min, float max, boolean toggle) { public static final OSCBinding EMPTY = new OSCBinding("", 0, 1, false); diff --git a/src/main/java/com/getpcpanel/profile/dto/OSCConnectionInfo.java b/src/main/java/com/getpcpanel/integration/osc/dto/OSCConnectionInfo.java similarity index 57% rename from src/main/java/com/getpcpanel/profile/dto/OSCConnectionInfo.java rename to src/main/java/com/getpcpanel/integration/osc/dto/OSCConnectionInfo.java index d522db31..6faa51d2 100644 --- a/src/main/java/com/getpcpanel/profile/dto/OSCConnectionInfo.java +++ b/src/main/java/com/getpcpanel/integration/osc/dto/OSCConnectionInfo.java @@ -1,4 +1,4 @@ -package com.getpcpanel.profile.dto; +package com.getpcpanel.integration.osc.dto; public record OSCConnectionInfo(String host, int port) { } diff --git a/src/main/java/com/getpcpanel/rest/OscResource.java b/src/main/java/com/getpcpanel/integration/osc/rest/OscResource.java similarity index 87% rename from src/main/java/com/getpcpanel/rest/OscResource.java rename to src/main/java/com/getpcpanel/integration/osc/rest/OscResource.java index 0dca117b..480d5355 100644 --- a/src/main/java/com/getpcpanel/rest/OscResource.java +++ b/src/main/java/com/getpcpanel/integration/osc/rest/OscResource.java @@ -1,8 +1,8 @@ -package com.getpcpanel.rest; +package com.getpcpanel.integration.osc.rest; import java.util.Map; -import com.getpcpanel.osc.OSCService; +import com.getpcpanel.integration.osc.OSCService; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandHttpRequest.java b/src/main/java/com/getpcpanel/integration/output/command/CommandHttpRequest.java similarity index 88% rename from src/main/java/com/getpcpanel/commands/command/CommandHttpRequest.java rename to src/main/java/com/getpcpanel/integration/output/command/CommandHttpRequest.java index f899194f..dc9bfd46 100644 Binary files a/src/main/java/com/getpcpanel/commands/command/CommandHttpRequest.java and b/src/main/java/com/getpcpanel/integration/output/command/CommandHttpRequest.java differ diff --git a/src/main/java/com/getpcpanel/integration/output/command/OutputCommandModule.java b/src/main/java/com/getpcpanel/integration/output/command/OutputCommandModule.java new file mode 100644 index 00000000..8a7b0731 --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/output/command/OutputCommandModule.java @@ -0,0 +1,21 @@ +package com.getpcpanel.integration.output.command; + +import java.util.List; + +import com.getpcpanel.commands.CommandModule; +import com.getpcpanel.commands.command.Command; + +import jakarta.enterprise.context.ApplicationScoped; + +/** + * Output feature module: registers its own command types via the {@link com.getpcpanel.commands.CommandModule} + * SPI. Adding/removing a command touches only this package. + */ +@ApplicationScoped +public class OutputCommandModule implements CommandModule { + @Override + public List> commandTypes() { + return List.of( + CommandHttpRequest.class); + } +} diff --git a/src/main/java/com/getpcpanel/commands/command/CommandProfile.java b/src/main/java/com/getpcpanel/integration/profile/command/CommandProfile.java similarity index 58% rename from src/main/java/com/getpcpanel/commands/command/CommandProfile.java rename to src/main/java/com/getpcpanel/integration/profile/command/CommandProfile.java index 0b98b04a..d16341f7 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandProfile.java +++ b/src/main/java/com/getpcpanel/integration/profile/command/CommandProfile.java @@ -1,18 +1,26 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.profile.command; +import com.getpcpanel.commands.command.Command; +import com.getpcpanel.commands.command.DeviceAction; import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; -import com.getpcpanel.hid.DeviceHolder; +import com.getpcpanel.device.DeviceHolder; import com.getpcpanel.util.CdiHelper; import lombok.Getter; import lombok.ToString; @ToString(callSuper = true) +@JsonTypeName("profile.switch") +@CommandMeta(label = "Switch profile", category = CommandCategory.system, kinds = {CommandKind.button}, icon = "refresh", legacyIds = {"com.getpcpanel.commands.command.CommandProfile"}) public class CommandProfile extends Command implements DeviceAction { @Getter private final String profile; diff --git a/src/main/java/com/getpcpanel/integration/profile/command/ProfileCommandModule.java b/src/main/java/com/getpcpanel/integration/profile/command/ProfileCommandModule.java new file mode 100644 index 00000000..61252334 --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/profile/command/ProfileCommandModule.java @@ -0,0 +1,21 @@ +package com.getpcpanel.integration.profile.command; + +import java.util.List; + +import com.getpcpanel.commands.CommandModule; +import com.getpcpanel.commands.command.Command; + +import jakarta.enterprise.context.ApplicationScoped; + +/** + * Profile feature module: registers its own command types via the {@link com.getpcpanel.commands.CommandModule} + * SPI. Adding/removing a command touches only this package. + */ +@ApplicationScoped +public class ProfileCommandModule implements CommandModule { + @Override + public List> commandTypes() { + return List.of( + CommandProfile.class); + } +} diff --git a/src/main/java/com/getpcpanel/util/IPlatformCommand.java b/src/main/java/com/getpcpanel/integration/program/IPlatformCommand.java similarity index 94% rename from src/main/java/com/getpcpanel/util/IPlatformCommand.java rename to src/main/java/com/getpcpanel/integration/program/IPlatformCommand.java index 56db2108..f676b800 100644 --- a/src/main/java/com/getpcpanel/util/IPlatformCommand.java +++ b/src/main/java/com/getpcpanel/integration/program/IPlatformCommand.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.integration.program; import java.io.File; import java.io.IOException; @@ -9,9 +9,10 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.cpp.linux.LinuxProcessHelper; -import com.getpcpanel.cpp.osx.OsxProcessHelper; +import com.getpcpanel.integration.volume.platform.ISndCtrl; +import com.getpcpanel.platform.process.LinuxProcessHelper; +import com.getpcpanel.platform.process.OsxProcessHelper; +import com.getpcpanel.util.Util; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandEndProgram.java b/src/main/java/com/getpcpanel/integration/program/command/CommandEndProgram.java similarity index 58% rename from src/main/java/com/getpcpanel/commands/command/CommandEndProgram.java rename to src/main/java/com/getpcpanel/integration/program/command/CommandEndProgram.java index ba6d5df4..f4b6b90c 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandEndProgram.java +++ b/src/main/java/com/getpcpanel/integration/program/command/CommandEndProgram.java @@ -1,9 +1,15 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.program.command; +import com.getpcpanel.commands.command.Command; +import com.getpcpanel.commands.command.ButtonAction; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.util.CdiHelper; -import com.getpcpanel.util.IPlatformCommand; +import com.getpcpanel.integration.program.IPlatformCommand; import lombok.Getter; import lombok.ToString; @@ -12,6 +18,8 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("program.end-program") +@CommandMeta(label = "End program", category = CommandCategory.system, kinds = {CommandKind.button}, icon = "x", legacyIds = {"com.getpcpanel.commands.command.CommandEndProgram"}) public class CommandEndProgram extends Command implements ButtonAction { private static final Runtime rt = Runtime.getRuntime(); private final boolean specific; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandRun.java b/src/main/java/com/getpcpanel/integration/program/command/CommandRun.java similarity index 52% rename from src/main/java/com/getpcpanel/commands/command/CommandRun.java rename to src/main/java/com/getpcpanel/integration/program/command/CommandRun.java index 3b49932c..d66bd9b9 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandRun.java +++ b/src/main/java/com/getpcpanel/integration/program/command/CommandRun.java @@ -1,9 +1,15 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.program.command; +import com.getpcpanel.commands.command.Command; +import com.getpcpanel.commands.command.ButtonAction; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.util.CdiHelper; -import com.getpcpanel.util.IPlatformCommand; +import com.getpcpanel.integration.program.IPlatformCommand; import lombok.Getter; import lombok.ToString; @@ -12,6 +18,8 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("program.run") +@CommandMeta(label = "Run command", category = CommandCategory.system, kinds = {CommandKind.button}, icon = "zap", legacyIds = {"com.getpcpanel.commands.command.CommandRun"}) public class CommandRun extends Command implements ButtonAction { private final String command; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandShortcut.java b/src/main/java/com/getpcpanel/integration/program/command/CommandShortcut.java similarity index 54% rename from src/main/java/com/getpcpanel/commands/command/CommandShortcut.java rename to src/main/java/com/getpcpanel/integration/program/command/CommandShortcut.java index 31d77282..ec1828e1 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandShortcut.java +++ b/src/main/java/com/getpcpanel/integration/program/command/CommandShortcut.java @@ -1,9 +1,15 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.program.command; +import com.getpcpanel.commands.command.Command; +import com.getpcpanel.commands.command.ButtonAction; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.util.CdiHelper; -import com.getpcpanel.util.IPlatformCommand; +import com.getpcpanel.integration.program.IPlatformCommand; import lombok.Getter; import lombok.ToString; @@ -12,6 +18,8 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("program.shortcut") +@CommandMeta(label = "Run shortcut", category = CommandCategory.system, kinds = {CommandKind.button}, icon = "zap", legacyIds = {"com.getpcpanel.commands.command.CommandShortcut"}) public class CommandShortcut extends Command implements ButtonAction { private static final Runtime rt = Runtime.getRuntime(); private final String shortcut; diff --git a/src/main/java/com/getpcpanel/integration/program/command/ProgramCommandModule.java b/src/main/java/com/getpcpanel/integration/program/command/ProgramCommandModule.java new file mode 100644 index 00000000..6b9803aa --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/program/command/ProgramCommandModule.java @@ -0,0 +1,23 @@ +package com.getpcpanel.integration.program.command; + +import java.util.List; + +import com.getpcpanel.commands.CommandModule; +import com.getpcpanel.commands.command.Command; + +import jakarta.enterprise.context.ApplicationScoped; + +/** + * Program feature module: registers its own command types via the {@link com.getpcpanel.commands.CommandModule} + * SPI. Adding/removing a command touches only this package. + */ +@ApplicationScoped +public class ProgramCommandModule implements CommandModule { + @Override + public List> commandTypes() { + return List.of( + CommandRun.class, + CommandShortcut.class, + CommandEndProgram.class); + } +} diff --git a/src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterConnectedEvent.java b/src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterConnectedEvent.java new file mode 100644 index 00000000..00bf8e55 --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterConnectedEvent.java @@ -0,0 +1,4 @@ +package com.getpcpanel.integration.voicemeeter; + +public record VoiceMeeterConnectedEvent() { +} diff --git a/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterConnectedVolumeService.java b/src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterConnectedVolumeService.java similarity index 57% rename from src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterConnectedVolumeService.java rename to src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterConnectedVolumeService.java index 35df4d38..5b4bd82f 100644 --- a/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterConnectedVolumeService.java +++ b/src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterConnectedVolumeService.java @@ -1,9 +1,9 @@ -package com.getpcpanel.voicemeeter; +package com.getpcpanel.integration.voicemeeter; -import com.getpcpanel.commands.command.CommandVoiceMeeter; -import com.getpcpanel.commands.command.CommandVoiceMeeterAdvanced; -import com.getpcpanel.commands.command.CommandVoiceMeeterBasic; -import com.getpcpanel.hid.DeviceHolder; +import com.getpcpanel.integration.voicemeeter.command.CommandVoiceMeeter; +import com.getpcpanel.integration.voicemeeter.command.CommandVoiceMeeterAdvanced; +import com.getpcpanel.integration.voicemeeter.command.CommandVoiceMeeterBasic; +import com.getpcpanel.device.DeviceHolder; import com.getpcpanel.platform.WindowsBuild; import jakarta.enterprise.context.ApplicationScoped; @@ -12,7 +12,7 @@ @ApplicationScoped @WindowsBuild -public class VoiceMeeterConnectedVolumeService { +class VoiceMeeterConnectedVolumeService { @Inject DeviceHolder devices; public void onVoiceMeeterConnected(@Observes VoiceMeeterConnectedEvent event) { diff --git a/src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterDirtyEvent.java b/src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterDirtyEvent.java new file mode 100644 index 00000000..5530e16c --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterDirtyEvent.java @@ -0,0 +1,4 @@ +package com.getpcpanel.integration.voicemeeter; + +public record VoiceMeeterDirtyEvent() { +} diff --git a/src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterIconHandler.java b/src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterIconHandler.java new file mode 100644 index 00000000..0da5e629 --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterIconHandler.java @@ -0,0 +1,28 @@ +package com.getpcpanel.integration.voicemeeter; + +import java.awt.image.BufferedImage; +import java.util.Optional; + +import com.getpcpanel.commands.IIconHandler; +import com.getpcpanel.commands.IconService; +import com.getpcpanel.integration.voicemeeter.command.CommandVoiceMeeter; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +/** Supplies the VoiceMeeter icon for any VoiceMeeter command, contributed via the {@link IIconHandler} SPI. */ +@ApplicationScoped +class VoiceMeeterIconHandler implements IIconHandler { + @Inject + IconService iconService; + + @Override + public Class getCommandClass() { + return CommandVoiceMeeter.class; + } + + @Override + public Optional supplyImage(CommandVoiceMeeter cmd) { + return Optional.ofNullable(iconService.VOICEMEETER); + } +} diff --git a/src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterMuteEvent.java b/src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterMuteEvent.java new file mode 100644 index 00000000..a1a5b518 --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterMuteEvent.java @@ -0,0 +1,7 @@ +package com.getpcpanel.integration.voicemeeter; + +import com.getpcpanel.integration.voicemeeter.Voicemeeter.ButtonType; +import com.getpcpanel.integration.voicemeeter.Voicemeeter.ControlType; + +public record VoiceMeeterMuteEvent(ControlType ct, int idx, ButtonType button, boolean state) { +} diff --git a/src/main/java/com/getpcpanel/mutecolor/VoiceMeeterMuteResolver.java b/src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterMuteResolver.java similarity index 86% rename from src/main/java/com/getpcpanel/mutecolor/VoiceMeeterMuteResolver.java rename to src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterMuteResolver.java index 7a50824c..37cd5885 100644 --- a/src/main/java/com/getpcpanel/mutecolor/VoiceMeeterMuteResolver.java +++ b/src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterMuteResolver.java @@ -1,4 +1,4 @@ -package com.getpcpanel.mutecolor; +package com.getpcpanel.integration.voicemeeter; import java.util.Map; import java.util.Optional; @@ -9,11 +9,12 @@ import org.apache.commons.lang3.math.NumberUtils; import com.getpcpanel.commands.Commands; -import com.getpcpanel.commands.command.CommandVoiceMeeterAdvanced; -import com.getpcpanel.commands.command.CommandVoiceMeeterBasic; -import com.getpcpanel.voicemeeter.VoiceMeeterMuteEvent; -import com.getpcpanel.voicemeeter.Voicemeeter.ButtonType; -import com.getpcpanel.voicemeeter.Voicemeeter.ControlType; +import com.getpcpanel.integration.volume.mutecolor.MuteOverridesDirtyEvent; +import com.getpcpanel.integration.volume.mutecolor.MuteStateResolver; +import com.getpcpanel.integration.voicemeeter.command.CommandVoiceMeeterAdvanced; +import com.getpcpanel.integration.voicemeeter.command.CommandVoiceMeeterBasic; +import com.getpcpanel.integration.voicemeeter.Voicemeeter.ButtonType; +import com.getpcpanel.integration.voicemeeter.Voicemeeter.ControlType; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Event; diff --git a/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterMuteService.java b/src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterMuteService.java similarity index 89% rename from src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterMuteService.java rename to src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterMuteService.java index 061cf62b..bcaa0363 100644 --- a/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterMuteService.java +++ b/src/main/java/com/getpcpanel/integration/voicemeeter/VoiceMeeterMuteService.java @@ -1,4 +1,4 @@ -package com.getpcpanel.voicemeeter; +package com.getpcpanel.integration.voicemeeter; import java.util.EnumMap; import java.util.HashMap; @@ -11,11 +11,11 @@ import jakarta.inject.Inject; import com.getpcpanel.profile.LightingChangedToDefaultEvent; -import com.getpcpanel.voicemeeter.Voicemeeter.ButtonType; -import com.getpcpanel.voicemeeter.Voicemeeter.ControlType; +import com.getpcpanel.integration.voicemeeter.Voicemeeter.ButtonType; +import com.getpcpanel.integration.voicemeeter.Voicemeeter.ControlType; @ApplicationScoped -public class VoiceMeeterMuteService { +class VoiceMeeterMuteService { @Inject Voicemeeter voiceMeeter; @Inject diff --git a/src/main/java/com/getpcpanel/voicemeeter/Voicemeeter.java b/src/main/java/com/getpcpanel/integration/voicemeeter/Voicemeeter.java similarity index 99% rename from src/main/java/com/getpcpanel/voicemeeter/Voicemeeter.java rename to src/main/java/com/getpcpanel/integration/voicemeeter/Voicemeeter.java index b3a2126f..d719ed64 100644 --- a/src/main/java/com/getpcpanel/voicemeeter/Voicemeeter.java +++ b/src/main/java/com/getpcpanel/integration/voicemeeter/Voicemeeter.java @@ -1,4 +1,4 @@ -package com.getpcpanel.voicemeeter; +package com.getpcpanel.integration.voicemeeter; import static com.getpcpanel.util.Util.map; diff --git a/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterAPI.java b/src/main/java/com/getpcpanel/integration/voicemeeter/VoicemeeterAPI.java similarity index 99% rename from src/main/java/com/getpcpanel/voicemeeter/VoicemeeterAPI.java rename to src/main/java/com/getpcpanel/integration/voicemeeter/VoicemeeterAPI.java index 41d53f68..b1c73fcc 100644 --- a/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterAPI.java +++ b/src/main/java/com/getpcpanel/integration/voicemeeter/VoicemeeterAPI.java @@ -2,7 +2,7 @@ * Wraps the VB-Audio Voicemeeter Remote API. * Adapted from Voicemeeter-JNA-Interface by mattco98 — https://github.com/mattco98/Voicemeeter-JNA-Interface */ -package com.getpcpanel.voicemeeter; +package com.getpcpanel.integration.voicemeeter; import java.io.File; import java.util.Set; @@ -20,7 +20,7 @@ @Log4j2 @ApplicationScoped @RequiredArgsConstructor -public final class VoicemeeterAPI { +final class VoicemeeterAPI { private final SaveService saveService; private VoicemeeterInstance instance; public final String DEFAULT_VM_WINDOWS_64BIT_PATH = "VoicemeeterRemote64.dll"; diff --git a/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterException.java b/src/main/java/com/getpcpanel/integration/voicemeeter/VoicemeeterException.java similarity index 82% rename from src/main/java/com/getpcpanel/voicemeeter/VoicemeeterException.java rename to src/main/java/com/getpcpanel/integration/voicemeeter/VoicemeeterException.java index 3f7d1c46..1431bf5d 100644 --- a/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterException.java +++ b/src/main/java/com/getpcpanel/integration/voicemeeter/VoicemeeterException.java @@ -1,11 +1,11 @@ /* * Adapted from Voicemeeter-JNA-Interface by mattco98 — https://github.com/mattco98/Voicemeeter-JNA-Interface */ -package com.getpcpanel.voicemeeter; +package com.getpcpanel.integration.voicemeeter; import lombok.Getter; -public class VoicemeeterException extends Exception { +class VoicemeeterException extends Exception { @Getter private final boolean disconnected; public VoicemeeterException(String message) { diff --git a/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterInstance.java b/src/main/java/com/getpcpanel/integration/voicemeeter/VoicemeeterInstance.java similarity index 99% rename from src/main/java/com/getpcpanel/voicemeeter/VoicemeeterInstance.java rename to src/main/java/com/getpcpanel/integration/voicemeeter/VoicemeeterInstance.java index c36dcfed..d2ee064d 100644 --- a/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterInstance.java +++ b/src/main/java/com/getpcpanel/integration/voicemeeter/VoicemeeterInstance.java @@ -3,7 +3,7 @@ * Copyright (c) Vincent Burel / VB-Audio Software. * Adapted from Voicemeeter-JNA-Interface by mattco98 — https://github.com/mattco98/Voicemeeter-JNA-Interface */ -package com.getpcpanel.voicemeeter; +package com.getpcpanel.integration.voicemeeter; import java.util.Arrays; import java.util.List; diff --git a/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterVersion.java b/src/main/java/com/getpcpanel/integration/voicemeeter/VoicemeeterVersion.java similarity index 99% rename from src/main/java/com/getpcpanel/voicemeeter/VoicemeeterVersion.java rename to src/main/java/com/getpcpanel/integration/voicemeeter/VoicemeeterVersion.java index c010ab07..2480facb 100644 --- a/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterVersion.java +++ b/src/main/java/com/getpcpanel/integration/voicemeeter/VoicemeeterVersion.java @@ -1,4 +1,4 @@ -package com.getpcpanel.voicemeeter; +package com.getpcpanel.integration.voicemeeter; public enum VoicemeeterVersion { POTATO { diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVoiceMeeter.java b/src/main/java/com/getpcpanel/integration/voicemeeter/command/CommandVoiceMeeter.java similarity index 58% rename from src/main/java/com/getpcpanel/commands/command/CommandVoiceMeeter.java rename to src/main/java/com/getpcpanel/integration/voicemeeter/command/CommandVoiceMeeter.java index 51d95e6e..ddc9057f 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVoiceMeeter.java +++ b/src/main/java/com/getpcpanel/integration/voicemeeter/command/CommandVoiceMeeter.java @@ -1,4 +1,6 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.voicemeeter.command; + +import com.getpcpanel.commands.command.Command; import lombok.Getter; import lombok.ToString; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVoiceMeeterAdvanced.java b/src/main/java/com/getpcpanel/integration/voicemeeter/command/CommandVoiceMeeterAdvanced.java similarity index 65% rename from src/main/java/com/getpcpanel/commands/command/CommandVoiceMeeterAdvanced.java rename to src/main/java/com/getpcpanel/integration/voicemeeter/command/CommandVoiceMeeterAdvanced.java index 251b54ab..c2b30ffa 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVoiceMeeterAdvanced.java +++ b/src/main/java/com/getpcpanel/integration/voicemeeter/command/CommandVoiceMeeterAdvanced.java @@ -1,15 +1,22 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.voicemeeter.command; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; +import com.getpcpanel.commands.command.DialAction; import com.getpcpanel.util.CdiHelper; -import com.getpcpanel.voicemeeter.Voicemeeter; +import com.getpcpanel.integration.voicemeeter.Voicemeeter; import lombok.Getter; import lombok.ToString; @Getter @ToString(callSuper = true) +@JsonTypeName("voicemeeter.advanced") +@CommandMeta(label = "Voicemeeter — parameter", category = CommandCategory.integration, kinds = {CommandKind.dial}, integration = "voicemeeter", icon = "sliders", legacyIds = {"com.getpcpanel.commands.command.CommandVoiceMeeterAdvanced"}) public class CommandVoiceMeeterAdvanced extends CommandVoiceMeeter implements DialAction { private final String fullParam; private final Voicemeeter.DialControlMode ct; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVoiceMeeterAdvancedButton.java b/src/main/java/com/getpcpanel/integration/voicemeeter/command/CommandVoiceMeeterAdvancedButton.java similarity index 65% rename from src/main/java/com/getpcpanel/commands/command/CommandVoiceMeeterAdvancedButton.java rename to src/main/java/com/getpcpanel/integration/voicemeeter/command/CommandVoiceMeeterAdvancedButton.java index 14c8c4c2..8f28d89e 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVoiceMeeterAdvancedButton.java +++ b/src/main/java/com/getpcpanel/integration/voicemeeter/command/CommandVoiceMeeterAdvancedButton.java @@ -1,19 +1,26 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.voicemeeter.command; import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; +import com.getpcpanel.commands.command.ButtonAction; import com.getpcpanel.util.CdiHelper; -import com.getpcpanel.voicemeeter.Voicemeeter; +import com.getpcpanel.integration.voicemeeter.Voicemeeter; import lombok.Getter; import lombok.ToString; @Getter @ToString(callSuper = true) +@JsonTypeName("voicemeeter.advanced-button") +@CommandMeta(label = "Voicemeeter — button", category = CommandCategory.integration, kinds = {CommandKind.button}, integration = "voicemeeter", icon = "sliders", legacyIds = {"com.getpcpanel.commands.command.CommandVoiceMeeterAdvancedButton"}) public class CommandVoiceMeeterAdvancedButton extends CommandVoiceMeeter implements ButtonAction { private final String fullParam; private final Voicemeeter.ButtonControlMode bt; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVoiceMeeterBasic.java b/src/main/java/com/getpcpanel/integration/voicemeeter/command/CommandVoiceMeeterBasic.java similarity index 81% rename from src/main/java/com/getpcpanel/commands/command/CommandVoiceMeeterBasic.java rename to src/main/java/com/getpcpanel/integration/voicemeeter/command/CommandVoiceMeeterBasic.java index 7063ac97..51020b40 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVoiceMeeterBasic.java +++ b/src/main/java/com/getpcpanel/integration/voicemeeter/command/CommandVoiceMeeterBasic.java @@ -1,15 +1,18 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.voicemeeter.command; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonProperty; +import com.getpcpanel.commands.command.DialAction; import com.getpcpanel.util.CdiHelper; -import com.getpcpanel.voicemeeter.Voicemeeter; +import com.getpcpanel.integration.voicemeeter.Voicemeeter; import lombok.Getter; import lombok.ToString; @Getter @ToString(callSuper = true) +@JsonTypeName("com.getpcpanel.commands.command.CommandVoiceMeeterBasic") public class CommandVoiceMeeterBasic extends CommandVoiceMeeter implements DialAction { private final Voicemeeter.ControlType ct; private final int index; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVoiceMeeterBasicButton.java b/src/main/java/com/getpcpanel/integration/voicemeeter/command/CommandVoiceMeeterBasicButton.java similarity index 78% rename from src/main/java/com/getpcpanel/commands/command/CommandVoiceMeeterBasicButton.java rename to src/main/java/com/getpcpanel/integration/voicemeeter/command/CommandVoiceMeeterBasicButton.java index ef27f022..8b15b11f 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVoiceMeeterBasicButton.java +++ b/src/main/java/com/getpcpanel/integration/voicemeeter/command/CommandVoiceMeeterBasicButton.java @@ -1,15 +1,18 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.voicemeeter.command; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonProperty; +import com.getpcpanel.commands.command.ButtonAction; import com.getpcpanel.util.CdiHelper; -import com.getpcpanel.voicemeeter.Voicemeeter; +import com.getpcpanel.integration.voicemeeter.Voicemeeter; import lombok.Getter; import lombok.ToString; @Getter @ToString(callSuper = true) +@JsonTypeName("com.getpcpanel.commands.command.CommandVoiceMeeterBasicButton") public class CommandVoiceMeeterBasicButton extends CommandVoiceMeeter implements ButtonAction { private final Voicemeeter.ControlType ct; private final int index; diff --git a/src/main/java/com/getpcpanel/integration/voicemeeter/command/VoiceMeeterCommandModule.java b/src/main/java/com/getpcpanel/integration/voicemeeter/command/VoiceMeeterCommandModule.java new file mode 100644 index 00000000..10656470 --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/voicemeeter/command/VoiceMeeterCommandModule.java @@ -0,0 +1,24 @@ +package com.getpcpanel.integration.voicemeeter.command; + +import java.util.List; + +import com.getpcpanel.commands.CommandModule; +import com.getpcpanel.commands.command.Command; + +import jakarta.enterprise.context.ApplicationScoped; + +/** + * VoiceMeeter feature module: registers its own command types via the + * {@link com.getpcpanel.commands.CommandModule} SPI. Adding/removing a command touches only this package. + */ +@ApplicationScoped +public class VoiceMeeterCommandModule implements CommandModule { + @Override + public List> commandTypes() { + return List.of( + CommandVoiceMeeterAdvanced.class, + CommandVoiceMeeterAdvancedButton.class, + CommandVoiceMeeterBasic.class, + CommandVoiceMeeterBasicButton.class); + } +} diff --git a/src/main/java/com/getpcpanel/rest/VoiceMeeterResource.java b/src/main/java/com/getpcpanel/integration/voicemeeter/rest/VoiceMeeterResource.java similarity index 94% rename from src/main/java/com/getpcpanel/rest/VoiceMeeterResource.java rename to src/main/java/com/getpcpanel/integration/voicemeeter/rest/VoiceMeeterResource.java index 5e4792b2..a6108c62 100644 --- a/src/main/java/com/getpcpanel/rest/VoiceMeeterResource.java +++ b/src/main/java/com/getpcpanel/integration/voicemeeter/rest/VoiceMeeterResource.java @@ -1,4 +1,4 @@ -package com.getpcpanel.rest; +package com.getpcpanel.integration.voicemeeter.rest; import java.util.List; diff --git a/src/main/java/com/getpcpanel/rest/AudioResource.java b/src/main/java/com/getpcpanel/integration/volume/AudioResource.java similarity index 92% rename from src/main/java/com/getpcpanel/rest/AudioResource.java rename to src/main/java/com/getpcpanel/integration/volume/AudioResource.java index c9f7b6cc..15d2d69d 100644 --- a/src/main/java/com/getpcpanel/rest/AudioResource.java +++ b/src/main/java/com/getpcpanel/integration/volume/AudioResource.java @@ -1,4 +1,4 @@ -package com.getpcpanel.rest; +package com.getpcpanel.integration.volume; import java.util.Collection; import java.util.LinkedHashMap; @@ -13,10 +13,10 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.AudioSession; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.cpp.MuteType; +import com.getpcpanel.integration.volume.platform.AudioDevice; +import com.getpcpanel.integration.volume.platform.AudioSession; +import com.getpcpanel.integration.volume.platform.ISndCtrl; +import com.getpcpanel.integration.volume.platform.MuteType; import lombok.extern.log4j.Log4j2; import one.util.streamex.StreamEx; diff --git a/src/main/java/com/getpcpanel/rest/FocusVolumeDiagnosticsResource.java b/src/main/java/com/getpcpanel/integration/volume/FocusVolumeDiagnosticsResource.java similarity index 94% rename from src/main/java/com/getpcpanel/rest/FocusVolumeDiagnosticsResource.java rename to src/main/java/com/getpcpanel/integration/volume/FocusVolumeDiagnosticsResource.java index a12ac681..32f04407 100644 --- a/src/main/java/com/getpcpanel/rest/FocusVolumeDiagnosticsResource.java +++ b/src/main/java/com/getpcpanel/integration/volume/FocusVolumeDiagnosticsResource.java @@ -1,4 +1,4 @@ -package com.getpcpanel.rest; +package com.getpcpanel.integration.volume; import java.util.LinkedHashMap; import java.util.Map; @@ -12,11 +12,10 @@ import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; -import com.getpcpanel.cpp.ISndCtrl; +import com.getpcpanel.integration.volume.platform.ISndCtrl; import com.getpcpanel.profile.SaveService; -import com.getpcpanel.volume.VolumeCoordinatorService; -import com.getpcpanel.wavelink.WaveLinkAppCache; -import com.getpcpanel.wavelink.WaveLinkService; +import com.getpcpanel.integration.wavelink.WaveLinkAppCache; +import com.getpcpanel.integration.wavelink.WaveLinkService; import dev.niels.wavelink.impl.model.WaveLinkApp; import dev.niels.wavelink.impl.model.WaveLinkChannel; diff --git a/src/main/java/com/getpcpanel/profile/dto/FocusVolumeOverride.java b/src/main/java/com/getpcpanel/integration/volume/FocusVolumeOverride.java similarity index 95% rename from src/main/java/com/getpcpanel/profile/dto/FocusVolumeOverride.java rename to src/main/java/com/getpcpanel/integration/volume/FocusVolumeOverride.java index 371fc1fe..b463ee4d 100644 --- a/src/main/java/com/getpcpanel/profile/dto/FocusVolumeOverride.java +++ b/src/main/java/com/getpcpanel/integration/volume/FocusVolumeOverride.java @@ -1,4 +1,4 @@ -package com.getpcpanel.profile.dto; +package com.getpcpanel.integration.volume; import java.util.List; diff --git a/src/main/java/com/getpcpanel/volume/FocusVolumeOverrideService.java b/src/main/java/com/getpcpanel/integration/volume/FocusVolumeOverrideService.java similarity index 94% rename from src/main/java/com/getpcpanel/volume/FocusVolumeOverrideService.java rename to src/main/java/com/getpcpanel/integration/volume/FocusVolumeOverrideService.java index 77ff3e76..bd6b2a00 100644 --- a/src/main/java/com/getpcpanel/volume/FocusVolumeOverrideService.java +++ b/src/main/java/com/getpcpanel/integration/volume/FocusVolumeOverrideService.java @@ -1,4 +1,4 @@ -package com.getpcpanel.volume; +package com.getpcpanel.integration.volume; import java.util.List; @@ -6,14 +6,12 @@ import org.apache.commons.lang3.StringUtils; import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandVolumeProcess; +import com.getpcpanel.integration.volume.command.CommandVolumeProcess; import com.getpcpanel.commands.command.DialAction; import com.getpcpanel.commands.command.DialAction.DialActionParameters; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.hid.DialValue; +import com.getpcpanel.integration.volume.platform.ISndCtrl; +import com.getpcpanel.commands.DialValue; import com.getpcpanel.profile.SaveService; -import com.getpcpanel.profile.dto.FocusVolumeOverride; -import com.getpcpanel.profile.dto.FocusVolumeTarget; import com.getpcpanel.profile.dto.KnobSetting; import io.quarkus.arc.Unremovable; @@ -35,7 +33,7 @@ @Log4j2 @Unremovable @ApplicationScoped -public class FocusVolumeOverrideService { +class FocusVolumeOverrideService { /** Audio device selector meaning "every render device" in {@link ISndCtrl#setProcessVolume}. */ private static final String ALL_DEVICES = "*"; diff --git a/src/main/java/com/getpcpanel/profile/dto/FocusVolumeTarget.java b/src/main/java/com/getpcpanel/integration/volume/FocusVolumeTarget.java similarity index 93% rename from src/main/java/com/getpcpanel/profile/dto/FocusVolumeTarget.java rename to src/main/java/com/getpcpanel/integration/volume/FocusVolumeTarget.java index 1cf606a7..bee08b06 100644 --- a/src/main/java/com/getpcpanel/profile/dto/FocusVolumeTarget.java +++ b/src/main/java/com/getpcpanel/integration/volume/FocusVolumeTarget.java @@ -1,4 +1,4 @@ -package com.getpcpanel.profile.dto; +package com.getpcpanel.integration.volume; import com.getpcpanel.commands.command.Command; diff --git a/src/main/java/com/getpcpanel/volume/IFocusRedirector.java b/src/main/java/com/getpcpanel/integration/volume/IFocusRedirector.java similarity index 95% rename from src/main/java/com/getpcpanel/volume/IFocusRedirector.java rename to src/main/java/com/getpcpanel/integration/volume/IFocusRedirector.java index 1d6e724a..ab773f4e 100644 --- a/src/main/java/com/getpcpanel/volume/IFocusRedirector.java +++ b/src/main/java/com/getpcpanel/integration/volume/IFocusRedirector.java @@ -1,4 +1,4 @@ -package com.getpcpanel.volume; +package com.getpcpanel.integration.volume; public interface IFocusRedirector { boolean handleFocusVolumeRequest(String targetProcess, float volume); diff --git a/src/main/java/com/getpcpanel/volume/LinuxNewSessionVolumeService.java b/src/main/java/com/getpcpanel/integration/volume/LinuxNewSessionVolumeService.java similarity index 86% rename from src/main/java/com/getpcpanel/volume/LinuxNewSessionVolumeService.java rename to src/main/java/com/getpcpanel/integration/volume/LinuxNewSessionVolumeService.java index 2ffc912a..27bf4975 100644 --- a/src/main/java/com/getpcpanel/volume/LinuxNewSessionVolumeService.java +++ b/src/main/java/com/getpcpanel/integration/volume/LinuxNewSessionVolumeService.java @@ -1,15 +1,15 @@ -package com.getpcpanel.volume; +package com.getpcpanel.integration.volume; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.StringUtils; -import com.getpcpanel.commands.command.CommandVolumeProcess; -import com.getpcpanel.cpp.AudioSessionEvent; -import com.getpcpanel.cpp.EventType; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.hid.DeviceHolder; +import com.getpcpanel.integration.volume.command.CommandVolumeProcess; +import com.getpcpanel.integration.volume.platform.AudioSessionEvent; +import com.getpcpanel.integration.volume.platform.EventType; +import com.getpcpanel.integration.volume.platform.ISndCtrl; +import com.getpcpanel.device.DeviceHolder; import com.getpcpanel.platform.LinuxBuild; import com.getpcpanel.profile.SaveService; @@ -21,7 +21,7 @@ @Log4j2 @LinuxBuild @ApplicationScoped -public class LinuxNewSessionVolumeService implements IFocusRedirector { +class LinuxNewSessionVolumeService implements IFocusRedirector { @Inject DeviceHolder devices; @Inject SaveService save; @Inject ISndCtrl sndCtrl; diff --git a/src/main/java/com/getpcpanel/rest/OverlayResource.java b/src/main/java/com/getpcpanel/integration/volume/OverlayResource.java similarity index 93% rename from src/main/java/com/getpcpanel/rest/OverlayResource.java rename to src/main/java/com/getpcpanel/integration/volume/OverlayResource.java index fc50c80a..cd8d02eb 100644 --- a/src/main/java/com/getpcpanel/rest/OverlayResource.java +++ b/src/main/java/com/getpcpanel/integration/volume/OverlayResource.java @@ -1,11 +1,11 @@ -package com.getpcpanel.rest; +package com.getpcpanel.integration.volume; import java.awt.GraphicsEnvironment; import java.util.Arrays; import java.util.List; -import com.getpcpanel.overlay.Overlay; -import com.getpcpanel.overlay.OverlayPreviewRenderer; +import com.getpcpanel.integration.volume.overlay.Overlay; +import com.getpcpanel.integration.volume.overlay.OverlayPreviewRenderer; import com.getpcpanel.profile.Save; import com.getpcpanel.rest.model.dto.SettingsDto; import com.sun.jna.Platform; diff --git a/src/main/java/com/getpcpanel/volume/VolumeCoordinatorService.java b/src/main/java/com/getpcpanel/integration/volume/VolumeCoordinatorService.java similarity index 97% rename from src/main/java/com/getpcpanel/volume/VolumeCoordinatorService.java rename to src/main/java/com/getpcpanel/integration/volume/VolumeCoordinatorService.java index 6b9e48fb..9deae12f 100644 --- a/src/main/java/com/getpcpanel/volume/VolumeCoordinatorService.java +++ b/src/main/java/com/getpcpanel/integration/volume/VolumeCoordinatorService.java @@ -1,4 +1,4 @@ -package com.getpcpanel.volume; +package com.getpcpanel.integration.volume; import java.io.File; import java.util.Optional; @@ -6,8 +6,8 @@ import org.apache.commons.lang3.StringUtils; import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandVolumeProcess; -import com.getpcpanel.cpp.ISndCtrl; +import com.getpcpanel.integration.volume.command.CommandVolumeProcess; +import com.getpcpanel.integration.volume.platform.ISndCtrl; import com.getpcpanel.profile.Profile; import com.getpcpanel.profile.SaveService; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVolume.java b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolume.java similarity index 68% rename from src/main/java/com/getpcpanel/commands/command/CommandVolume.java rename to src/main/java/com/getpcpanel/integration/volume/command/CommandVolume.java index d3e7025f..8de34ae7 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVolume.java +++ b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolume.java @@ -1,9 +1,10 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.volume.command; +import com.getpcpanel.commands.command.Command; import com.fasterxml.jackson.annotation.JsonIgnore; import com.getpcpanel.util.CdiHelper; import jakarta.inject.Inject; -import com.getpcpanel.cpp.ISndCtrl; +import com.getpcpanel.integration.volume.platform.ISndCtrl; import lombok.ToString; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVolumeApplicationDeviceToggle.java b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeApplicationDeviceToggle.java similarity index 87% rename from src/main/java/com/getpcpanel/commands/command/CommandVolumeApplicationDeviceToggle.java rename to src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeApplicationDeviceToggle.java index 48cd2bc7..7d3c1b72 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVolumeApplicationDeviceToggle.java +++ b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeApplicationDeviceToggle.java @@ -1,5 +1,6 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.volume.command; +import com.getpcpanel.commands.command.ButtonAction; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -10,11 +11,12 @@ import org.apache.commons.lang3.StringUtils; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.DeviceSet; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.DataFlow; -import com.getpcpanel.cpp.windows.SndCtrlWindows; +import com.getpcpanel.integration.volume.platform.AudioDevice; +import com.getpcpanel.integration.volume.platform.DataFlow; +import com.getpcpanel.integration.volume.platform.windows.SndCtrlWindows; import lombok.Getter; import lombok.ToString; @@ -24,6 +26,7 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("com.getpcpanel.commands.command.CommandVolumeApplicationDeviceToggle") public class CommandVolumeApplicationDeviceToggle extends CommandVolume implements ButtonAction { private final List processes; private final boolean followFocus; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDevice.java b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeDefaultDevice.java similarity index 62% rename from src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDevice.java rename to src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeDefaultDevice.java index acdbc1bd..d6035cd4 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDevice.java +++ b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeDefaultDevice.java @@ -1,8 +1,13 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.volume.command; +import com.getpcpanel.commands.command.ButtonAction; import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; @@ -10,6 +15,8 @@ @Getter @ToString(callSuper = true) +@JsonTypeName("volume.default-device") +@CommandMeta(label = "Set default device", category = CommandCategory.audio, kinds = {CommandKind.button}, icon = "monitor", legacyIds = {"com.getpcpanel.commands.command.CommandVolumeDefaultDevice"}) public class CommandVolumeDefaultDevice extends CommandVolume implements ButtonAction { private final String deviceId; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDeviceAdvanced.java b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeDefaultDeviceAdvanced.java similarity index 66% rename from src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDeviceAdvanced.java rename to src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeDefaultDeviceAdvanced.java index 841192a6..6210eb97 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDeviceAdvanced.java +++ b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeDefaultDeviceAdvanced.java @@ -1,11 +1,16 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.volume.command; +import com.getpcpanel.commands.command.ButtonAction; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import javax.annotation.Nullable; import com.getpcpanel.util.CdiHelper; -import com.getpcpanel.cpp.DataFlow; -import com.getpcpanel.cpp.Role; -import com.getpcpanel.cpp.windows.SndCtrlWindows; +import com.getpcpanel.integration.volume.platform.DataFlow; +import com.getpcpanel.integration.volume.platform.Role; +import com.getpcpanel.integration.volume.platform.windows.SndCtrlWindows; import lombok.AllArgsConstructor; import lombok.Builder; @@ -20,6 +25,8 @@ @Jacksonized @AllArgsConstructor @ToString(callSuper = true) +@JsonTypeName("volume.default-device-advanced") +@CommandMeta(label = "Advanced default device", category = CommandCategory.audio, kinds = {CommandKind.button}, icon = "monitor", legacyIds = {"com.getpcpanel.commands.command.CommandVolumeDefaultDeviceAdvanced"}) public class CommandVolumeDefaultDeviceAdvanced extends CommandVolume implements ButtonAction { private final String name; private final String mediaPb; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDeviceToggle.java b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeDefaultDeviceToggle.java similarity index 75% rename from src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDeviceToggle.java rename to src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeDefaultDeviceToggle.java index 45e04ff6..77b5dd9c 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDeviceToggle.java +++ b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeDefaultDeviceToggle.java @@ -1,5 +1,6 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.volume.command; +import com.getpcpanel.commands.command.ButtonAction; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -8,9 +9,13 @@ import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.DataFlow; +import com.getpcpanel.integration.volume.platform.AudioDevice; +import com.getpcpanel.integration.volume.platform.DataFlow; import lombok.Getter; import lombok.ToString; @@ -20,6 +25,8 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("volume.default-device-toggle") +@CommandMeta(label = "Cycle default device", category = CommandCategory.audio, kinds = {CommandKind.button}, icon = "refresh", legacyIds = {"com.getpcpanel.commands.command.CommandVolumeDefaultDeviceToggle"}) public class CommandVolumeDefaultDeviceToggle extends CommandVolume implements ButtonAction { private final List devices; private int currentIdx; // Used as a fallback for when the current idx cannot be found diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDeviceToggleAdvanced.java b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeDefaultDeviceToggleAdvanced.java similarity index 87% rename from src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDeviceToggleAdvanced.java rename to src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeDefaultDeviceToggleAdvanced.java index 17357927..47041f9b 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDeviceToggleAdvanced.java +++ b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeDefaultDeviceToggleAdvanced.java @@ -1,5 +1,6 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.volume.command; +import com.getpcpanel.commands.command.ButtonAction; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -10,12 +11,13 @@ import org.apache.commons.lang3.StringUtils; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.DeviceSet; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.DataFlow; -import com.getpcpanel.cpp.Role; -import com.getpcpanel.cpp.windows.SndCtrlWindows; +import com.getpcpanel.integration.volume.platform.AudioDevice; +import com.getpcpanel.integration.volume.platform.DataFlow; +import com.getpcpanel.integration.volume.platform.Role; +import com.getpcpanel.integration.volume.platform.windows.SndCtrlWindows; import lombok.Getter; import lombok.ToString; @@ -26,6 +28,7 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("com.getpcpanel.commands.command.CommandVolumeDefaultDeviceToggleAdvanced") public class CommandVolumeDefaultDeviceToggleAdvanced extends CommandVolume implements ButtonAction { private final List devices; private int currentIdx = -1; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVolumeDevice.java b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeDevice.java similarity index 68% rename from src/main/java/com/getpcpanel/commands/command/CommandVolumeDevice.java rename to src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeDevice.java index b8f218ad..a94b3416 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVolumeDevice.java +++ b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeDevice.java @@ -1,14 +1,21 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.volume.command; +import com.getpcpanel.commands.command.DialAction; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; -import com.getpcpanel.cpp.MuteType; +import com.getpcpanel.integration.volume.platform.MuteType; import lombok.Getter; import lombok.ToString; @Getter @ToString(callSuper = true) +@JsonTypeName("volume.device") +@CommandMeta(label = "Device volume", category = CommandCategory.audio, kinds = {CommandKind.dial}, icon = "volume", legacyIds = {"com.getpcpanel.commands.command.CommandVolumeDevice"}) public class CommandVolumeDevice extends CommandVolume implements DialAction { private final String deviceId; private final boolean unMuteOnVolumeChange; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVolumeDeviceMute.java b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeDeviceMute.java similarity index 56% rename from src/main/java/com/getpcpanel/commands/command/CommandVolumeDeviceMute.java rename to src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeDeviceMute.java index 191f8fc1..18c20919 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVolumeDeviceMute.java +++ b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeDeviceMute.java @@ -1,14 +1,21 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.volume.command; +import com.getpcpanel.commands.command.ButtonAction; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; -import com.getpcpanel.cpp.MuteType; +import com.getpcpanel.integration.volume.platform.MuteType; import lombok.Getter; import lombok.ToString; @Getter @ToString(callSuper = true) +@JsonTypeName("volume.device-mute") +@CommandMeta(label = "Device mute", category = CommandCategory.audio, kinds = {CommandKind.button}, icon = "volume-x", legacyIds = {"com.getpcpanel.commands.command.CommandVolumeDeviceMute"}) public class CommandVolumeDeviceMute extends CommandVolume implements ButtonAction { private final String deviceId; private final MuteType muteType; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVolumeFocus.java b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeFocus.java similarity index 57% rename from src/main/java/com/getpcpanel/commands/command/CommandVolumeFocus.java rename to src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeFocus.java index 29c77d35..7dd0e172 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVolumeFocus.java +++ b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeFocus.java @@ -1,8 +1,13 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.volume.command; +import com.getpcpanel.commands.command.DialAction; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; -import com.getpcpanel.volume.VolumeCoordinatorService; +import com.getpcpanel.integration.volume.VolumeCoordinatorService; import com.getpcpanel.util.CdiHelper; import lombok.Getter; @@ -10,6 +15,8 @@ @Getter @ToString(callSuper = true) +@JsonTypeName("volume.focus") +@CommandMeta(label = "Focused-app volume", category = CommandCategory.audio, kinds = {CommandKind.dial}, icon = "volume", legacyIds = {"com.getpcpanel.commands.command.CommandVolumeFocus"}) public class CommandVolumeFocus extends CommandVolume implements DialAction { private final DialCommandParams dialParams; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVolumeFocusMute.java b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeFocusMute.java similarity index 51% rename from src/main/java/com/getpcpanel/commands/command/CommandVolumeFocusMute.java rename to src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeFocusMute.java index bf71cc3f..aa172be8 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVolumeFocusMute.java +++ b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeFocusMute.java @@ -1,15 +1,22 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.volume.command; +import com.getpcpanel.commands.command.ButtonAction; import java.util.Set; import com.fasterxml.jackson.annotation.JsonProperty; -import com.getpcpanel.cpp.MuteType; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; +import com.getpcpanel.integration.volume.platform.MuteType; import lombok.Getter; import lombok.ToString; @Getter @ToString(callSuper = true) +@JsonTypeName("volume.focus-mute") +@CommandMeta(label = "Focused-app mute", category = CommandCategory.audio, kinds = {CommandKind.button}, icon = "volume-x", legacyIds = {"com.getpcpanel.commands.command.CommandVolumeFocusMute"}) public class CommandVolumeFocusMute extends CommandVolume implements ButtonAction { private final MuteType muteType; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVolumeProcess.java b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeProcess.java similarity index 71% rename from src/main/java/com/getpcpanel/commands/command/CommandVolumeProcess.java rename to src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeProcess.java index 598b0046..b2cf0cca 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVolumeProcess.java +++ b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeProcess.java @@ -1,17 +1,24 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.volume.command; +import com.getpcpanel.commands.command.DialAction; import java.util.HashSet; import java.util.List; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; -import com.getpcpanel.cpp.MuteType; +import com.getpcpanel.integration.volume.platform.MuteType; import lombok.Getter; import lombok.ToString; @Getter @ToString(callSuper = true) +@JsonTypeName("volume.process") +@CommandMeta(label = "App volume", category = CommandCategory.audio, kinds = {CommandKind.dial}, icon = "volume", legacyIds = {"com.getpcpanel.commands.command.CommandVolumeProcess"}) public class CommandVolumeProcess extends CommandVolume implements DialAction { private final List processName; private final String device; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVolumeProcessMute.java b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeProcessMute.java similarity index 55% rename from src/main/java/com/getpcpanel/commands/command/CommandVolumeProcessMute.java rename to src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeProcessMute.java index 20d6b717..766c87af 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVolumeProcessMute.java +++ b/src/main/java/com/getpcpanel/integration/volume/command/CommandVolumeProcessMute.java @@ -1,15 +1,22 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.volume.command; +import com.getpcpanel.commands.command.ButtonAction; import java.util.Set; import com.fasterxml.jackson.annotation.JsonProperty; -import com.getpcpanel.cpp.MuteType; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; +import com.getpcpanel.integration.volume.platform.MuteType; import lombok.Getter; import lombok.ToString; @Getter @ToString(callSuper = true) +@JsonTypeName("volume.process-mute") +@CommandMeta(label = "App mute", category = CommandCategory.audio, kinds = {CommandKind.button}, icon = "volume-x", legacyIds = {"com.getpcpanel.commands.command.CommandVolumeProcessMute"}) public class CommandVolumeProcessMute extends CommandVolume implements ButtonAction { private final Set processName; private final MuteType muteType; diff --git a/src/main/java/com/getpcpanel/integration/volume/command/VolumeCommandModule.java b/src/main/java/com/getpcpanel/integration/volume/command/VolumeCommandModule.java new file mode 100644 index 00000000..c695c84f --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/volume/command/VolumeCommandModule.java @@ -0,0 +1,31 @@ +package com.getpcpanel.integration.volume.command; + +import java.util.List; + +import com.getpcpanel.commands.CommandModule; +import com.getpcpanel.commands.command.Command; + +import jakarta.enterprise.context.ApplicationScoped; + +/** + * Volume feature module: registers its own command types via the {@link com.getpcpanel.commands.CommandModule} + * SPI. Adding/removing a command touches only this package. + */ +@ApplicationScoped +public class VolumeCommandModule implements CommandModule { + @Override + public List> commandTypes() { + return List.of( + CommandVolumeProcess.class, + CommandVolumeProcessMute.class, + CommandVolumeFocus.class, + CommandVolumeFocusMute.class, + CommandVolumeDevice.class, + CommandVolumeDeviceMute.class, + CommandVolumeDefaultDevice.class, + CommandVolumeDefaultDeviceToggle.class, + CommandVolumeDefaultDeviceAdvanced.class, + CommandVolumeDefaultDeviceToggleAdvanced.class, + CommandVolumeApplicationDeviceToggle.class); + } +} diff --git a/src/main/java/com/getpcpanel/mutecolor/DeviceMuteResolver.java b/src/main/java/com/getpcpanel/integration/volume/mutecolor/DeviceMuteResolver.java similarity index 76% rename from src/main/java/com/getpcpanel/mutecolor/DeviceMuteResolver.java rename to src/main/java/com/getpcpanel/integration/volume/mutecolor/DeviceMuteResolver.java index 0d13ce6b..65b8aba6 100644 --- a/src/main/java/com/getpcpanel/mutecolor/DeviceMuteResolver.java +++ b/src/main/java/com/getpcpanel/integration/volume/mutecolor/DeviceMuteResolver.java @@ -1,17 +1,17 @@ -package com.getpcpanel.mutecolor; +package com.getpcpanel.integration.volume.mutecolor; import java.util.Optional; import com.getpcpanel.commands.Commands; -import com.getpcpanel.commands.command.CommandVolumeDevice; -import com.getpcpanel.cpp.ISndCtrl; +import com.getpcpanel.integration.volume.command.CommandVolumeDevice; +import com.getpcpanel.integration.volume.platform.ISndCtrl; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; /** Mute state of an audio-device volume control ({@link CommandVolumeDevice}); empty deviceId = the default device. */ @ApplicationScoped -public class DeviceMuteResolver implements MuteStateResolver { +class DeviceMuteResolver implements MuteStateResolver { @Inject ISndCtrl sndCtrl; diff --git a/src/main/java/com/getpcpanel/mutecolor/MuteColorService.java b/src/main/java/com/getpcpanel/integration/volume/mutecolor/MuteColorService.java similarity index 96% rename from src/main/java/com/getpcpanel/mutecolor/MuteColorService.java rename to src/main/java/com/getpcpanel/integration/volume/mutecolor/MuteColorService.java index 6afc7c80..d9d877d9 100644 --- a/src/main/java/com/getpcpanel/mutecolor/MuteColorService.java +++ b/src/main/java/com/getpcpanel/integration/volume/mutecolor/MuteColorService.java @@ -1,4 +1,4 @@ -package com.getpcpanel.mutecolor; +package com.getpcpanel.integration.volume.mutecolor; import java.util.ArrayList; import java.util.HashMap; @@ -10,13 +10,13 @@ import com.getpcpanel.commands.Commands; import com.getpcpanel.commands.command.Command; -import com.getpcpanel.cpp.AudioDeviceEvent; -import com.getpcpanel.cpp.AudioSessionEvent; +import com.getpcpanel.integration.volume.platform.AudioDeviceEvent; +import com.getpcpanel.integration.volume.platform.AudioSessionEvent; import com.getpcpanel.device.Device; -import com.getpcpanel.discord.DiscordChangedEvent; -import com.getpcpanel.hid.DeviceHolder; -import com.getpcpanel.obs.OBSConnectEvent; -import com.getpcpanel.obs.OBSMuteEvent; +import com.getpcpanel.integration.discord.DiscordChangedEvent; +import com.getpcpanel.device.DeviceHolder; +import com.getpcpanel.integration.obs.OBSConnectEvent; +import com.getpcpanel.integration.obs.OBSMuteEvent; import com.getpcpanel.profile.BaseLayerService; import com.getpcpanel.profile.LightingChangedToDefaultEvent; import com.getpcpanel.profile.Profile; @@ -35,7 +35,7 @@ import com.getpcpanel.util.coloroverride.ColorOverrideHolder; import com.getpcpanel.util.coloroverride.IOverrideColorProvider; import com.getpcpanel.util.coloroverride.IOverrideColorProviderProvider; -import com.getpcpanel.wavelink.WaveLinkChangedEvent; +import com.getpcpanel.integration.wavelink.WaveLinkChangedEvent; import io.quarkus.arc.All; import jakarta.annotation.Priority; @@ -65,7 +65,7 @@ @Log4j2 @Priority(0) @ApplicationScoped -public class MuteColorService implements IOverrideColorProviderProvider { +class MuteColorService implements IOverrideColorProviderProvider { @Inject DeviceHolder devices; @Inject diff --git a/src/main/java/com/getpcpanel/mutecolor/MuteOverridesDirtyEvent.java b/src/main/java/com/getpcpanel/integration/volume/mutecolor/MuteOverridesDirtyEvent.java similarity index 87% rename from src/main/java/com/getpcpanel/mutecolor/MuteOverridesDirtyEvent.java rename to src/main/java/com/getpcpanel/integration/volume/mutecolor/MuteOverridesDirtyEvent.java index ebaef528..53edbeb0 100644 --- a/src/main/java/com/getpcpanel/mutecolor/MuteOverridesDirtyEvent.java +++ b/src/main/java/com/getpcpanel/integration/volume/mutecolor/MuteOverridesDirtyEvent.java @@ -1,4 +1,4 @@ -package com.getpcpanel.mutecolor; +package com.getpcpanel.integration.volume.mutecolor; /** * Fired when a resolver's internally-cached mute state has changed (e.g. a VoiceMeeter mute toggle) diff --git a/src/main/java/com/getpcpanel/mutecolor/MuteStateResolver.java b/src/main/java/com/getpcpanel/integration/volume/mutecolor/MuteStateResolver.java similarity index 96% rename from src/main/java/com/getpcpanel/mutecolor/MuteStateResolver.java rename to src/main/java/com/getpcpanel/integration/volume/mutecolor/MuteStateResolver.java index 9b505690..12d57f2e 100644 --- a/src/main/java/com/getpcpanel/mutecolor/MuteStateResolver.java +++ b/src/main/java/com/getpcpanel/integration/volume/mutecolor/MuteStateResolver.java @@ -1,4 +1,4 @@ -package com.getpcpanel.mutecolor; +package com.getpcpanel.integration.volume.mutecolor; import java.util.Optional; diff --git a/src/main/java/com/getpcpanel/mutecolor/NamedDeviceMuteResolver.java b/src/main/java/com/getpcpanel/integration/volume/mutecolor/NamedDeviceMuteResolver.java similarity index 83% rename from src/main/java/com/getpcpanel/mutecolor/NamedDeviceMuteResolver.java rename to src/main/java/com/getpcpanel/integration/volume/mutecolor/NamedDeviceMuteResolver.java index 294e6dc2..1e907741 100644 --- a/src/main/java/com/getpcpanel/mutecolor/NamedDeviceMuteResolver.java +++ b/src/main/java/com/getpcpanel/integration/volume/mutecolor/NamedDeviceMuteResolver.java @@ -1,11 +1,12 @@ -package com.getpcpanel.mutecolor; +package com.getpcpanel.integration.volume.mutecolor; import java.util.Optional; import org.apache.commons.lang3.StringUtils; import com.getpcpanel.commands.Commands; -import com.getpcpanel.cpp.ISndCtrl; +import com.getpcpanel.integration.voicemeeter.VoiceMeeterMuteResolver; +import com.getpcpanel.integration.volume.platform.ISndCtrl; import jakarta.annotation.Priority; import jakarta.enterprise.context.ApplicationScoped; @@ -19,7 +20,7 @@ */ @Priority(-100) @ApplicationScoped -public class NamedDeviceMuteResolver implements MuteStateResolver { +class NamedDeviceMuteResolver implements MuteStateResolver { @Inject ISndCtrl sndCtrl; diff --git a/src/main/java/com/getpcpanel/mutecolor/ProcessMuteResolver.java b/src/main/java/com/getpcpanel/integration/volume/mutecolor/ProcessMuteResolver.java similarity index 86% rename from src/main/java/com/getpcpanel/mutecolor/ProcessMuteResolver.java rename to src/main/java/com/getpcpanel/integration/volume/mutecolor/ProcessMuteResolver.java index 4eaf3db2..fac3881f 100644 --- a/src/main/java/com/getpcpanel/mutecolor/ProcessMuteResolver.java +++ b/src/main/java/com/getpcpanel/integration/volume/mutecolor/ProcessMuteResolver.java @@ -1,4 +1,4 @@ -package com.getpcpanel.mutecolor; +package com.getpcpanel.integration.volume.mutecolor; import java.util.List; import java.util.Optional; @@ -6,16 +6,16 @@ import org.apache.commons.lang3.StringUtils; import com.getpcpanel.commands.Commands; -import com.getpcpanel.commands.command.CommandVolumeProcess; -import com.getpcpanel.cpp.AudioSession; -import com.getpcpanel.cpp.ISndCtrl; +import com.getpcpanel.integration.volume.command.CommandVolumeProcess; +import com.getpcpanel.integration.volume.platform.AudioSession; +import com.getpcpanel.integration.volume.platform.ISndCtrl; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; /** Mute state of a per-application volume control ({@link CommandVolumeProcess}), incl. "System Sounds". */ @ApplicationScoped -public class ProcessMuteResolver implements MuteStateResolver { +class ProcessMuteResolver implements MuteStateResolver { @Inject ISndCtrl sndCtrl; diff --git a/src/main/java/com/getpcpanel/overlay/KdeOsdService.java b/src/main/java/com/getpcpanel/integration/volume/overlay/KdeOsdService.java similarity index 95% rename from src/main/java/com/getpcpanel/overlay/KdeOsdService.java rename to src/main/java/com/getpcpanel/integration/volume/overlay/KdeOsdService.java index b3389fc8..4c9718a3 100644 --- a/src/main/java/com/getpcpanel/overlay/KdeOsdService.java +++ b/src/main/java/com/getpcpanel/integration/volume/overlay/KdeOsdService.java @@ -1,4 +1,4 @@ -package com.getpcpanel.overlay; +package com.getpcpanel.integration.volume.overlay; import org.freedesktop.dbus.annotations.DBusInterfaceName; import org.freedesktop.dbus.interfaces.DBusInterface; diff --git a/src/main/java/com/getpcpanel/overlay/LinuxOverlay.java b/src/main/java/com/getpcpanel/integration/volume/overlay/LinuxOverlay.java similarity index 98% rename from src/main/java/com/getpcpanel/overlay/LinuxOverlay.java rename to src/main/java/com/getpcpanel/integration/volume/overlay/LinuxOverlay.java index ac1a19cd..890b674b 100644 --- a/src/main/java/com/getpcpanel/overlay/LinuxOverlay.java +++ b/src/main/java/com/getpcpanel/integration/volume/overlay/LinuxOverlay.java @@ -1,4 +1,4 @@ -package com.getpcpanel.overlay; +package com.getpcpanel.integration.volume.overlay; import java.nio.file.Files; import java.nio.file.Path; @@ -38,7 +38,7 @@ * OSD's appearance and placement. The settings UI greys those out on Linux. */ @Log4j2 -public class LinuxOverlay implements OverlayWindow { +class LinuxOverlay implements OverlayWindow { private static final String PLASMA_BUS = "org.kde.plasmashell"; private static final String OSD_PATH = "/org/kde/osdService"; /** Give up (and stop logging) only after this many consecutive failures, not a single hiccup. */ diff --git a/src/main/java/com/getpcpanel/overlay/NoOpOverlayWindow.java b/src/main/java/com/getpcpanel/integration/volume/overlay/NoOpOverlayWindow.java similarity index 91% rename from src/main/java/com/getpcpanel/overlay/NoOpOverlayWindow.java rename to src/main/java/com/getpcpanel/integration/volume/overlay/NoOpOverlayWindow.java index 989fd360..23f94b33 100644 --- a/src/main/java/com/getpcpanel/overlay/NoOpOverlayWindow.java +++ b/src/main/java/com/getpcpanel/integration/volume/overlay/NoOpOverlayWindow.java @@ -1,4 +1,4 @@ -package com.getpcpanel.overlay; +package com.getpcpanel.integration.volume.overlay; import com.getpcpanel.profile.Save; @@ -10,7 +10,7 @@ *

Returns a plain {@link ScreenSize} value and never touches an AWT type, so it is safe in the * libawt-less native images on macOS and Linux. */ -public class NoOpOverlayWindow implements OverlayWindow { +class NoOpOverlayWindow implements OverlayWindow { @Override public void show(OverlayContent content) { // no overlay on macOS diff --git a/src/main/java/com/getpcpanel/overlay/Overlay.java b/src/main/java/com/getpcpanel/integration/volume/overlay/Overlay.java similarity index 97% rename from src/main/java/com/getpcpanel/overlay/Overlay.java rename to src/main/java/com/getpcpanel/integration/volume/overlay/Overlay.java index 9ec1d574..87a2a5cd 100644 --- a/src/main/java/com/getpcpanel/overlay/Overlay.java +++ b/src/main/java/com/getpcpanel/integration/volume/overlay/Overlay.java @@ -1,4 +1,4 @@ -package com.getpcpanel.overlay; +package com.getpcpanel.integration.volume.overlay; import java.awt.Image; import java.io.File; @@ -12,21 +12,20 @@ import com.getpcpanel.commands.PCPanelControlEvent; import com.getpcpanel.commands.command.ButtonAction; import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandVolumeFocus; +import com.getpcpanel.integration.volume.command.CommandVolumeFocus; import com.getpcpanel.commands.command.DialAction; -import com.getpcpanel.cpp.ISndCtrl; +import com.getpcpanel.integration.volume.platform.ISndCtrl; import com.getpcpanel.device.Device; import com.getpcpanel.device.descriptor.AnalogKind; -import com.getpcpanel.hid.DeviceHolder; +import com.getpcpanel.device.DeviceHolder; import com.getpcpanel.profile.SaveService; import com.getpcpanel.profile.SaveService.SaveEvent; import com.getpcpanel.profile.dto.LightingConfig; -import com.getpcpanel.profile.dto.OverlayPosition; import com.getpcpanel.profile.dto.SingleKnobLightingConfig; import com.getpcpanel.profile.dto.SingleKnobLightingConfig.SINGLE_KNOB_MODE; import com.getpcpanel.profile.dto.SingleSliderLightingConfig; import com.getpcpanel.util.coloroverride.OverrideColorService; -import com.getpcpanel.volume.VolumeCoordinatorService; +import com.getpcpanel.integration.volume.VolumeCoordinatorService; import com.sun.jna.Platform; import jakarta.annotation.Nonnull; diff --git a/src/main/java/com/getpcpanel/overlay/OverlayContent.java b/src/main/java/com/getpcpanel/integration/volume/overlay/OverlayContent.java similarity index 92% rename from src/main/java/com/getpcpanel/overlay/OverlayContent.java rename to src/main/java/com/getpcpanel/integration/volume/overlay/OverlayContent.java index 9a37f1e1..115c9645 100644 --- a/src/main/java/com/getpcpanel/overlay/OverlayContent.java +++ b/src/main/java/com/getpcpanel/integration/volume/overlay/OverlayContent.java @@ -1,4 +1,4 @@ -package com.getpcpanel.overlay; +package com.getpcpanel.integration.volume.overlay; import java.awt.Image; diff --git a/src/main/java/com/getpcpanel/overlay/OverlayDemoTrigger.java b/src/main/java/com/getpcpanel/integration/volume/overlay/OverlayDemoTrigger.java similarity index 96% rename from src/main/java/com/getpcpanel/overlay/OverlayDemoTrigger.java rename to src/main/java/com/getpcpanel/integration/volume/overlay/OverlayDemoTrigger.java index be61c954..442c1d44 100644 --- a/src/main/java/com/getpcpanel/overlay/OverlayDemoTrigger.java +++ b/src/main/java/com/getpcpanel/integration/volume/overlay/OverlayDemoTrigger.java @@ -1,4 +1,4 @@ -package com.getpcpanel.overlay; +package com.getpcpanel.integration.volume.overlay; import com.sun.jna.Platform; @@ -17,7 +17,7 @@ */ @Log4j2 @ApplicationScoped -public class OverlayDemoTrigger { +class OverlayDemoTrigger { void onStart(@Observes StartupEvent event) { if (!Boolean.getBoolean("pcpanel.overlay.demo")) { return; diff --git a/src/main/java/com/getpcpanel/profile/dto/OverlayPosition.java b/src/main/java/com/getpcpanel/integration/volume/overlay/OverlayPosition.java similarity index 84% rename from src/main/java/com/getpcpanel/profile/dto/OverlayPosition.java rename to src/main/java/com/getpcpanel/integration/volume/overlay/OverlayPosition.java index 8170d8b5..5354702e 100644 --- a/src/main/java/com/getpcpanel/profile/dto/OverlayPosition.java +++ b/src/main/java/com/getpcpanel/integration/volume/overlay/OverlayPosition.java @@ -1,4 +1,4 @@ -package com.getpcpanel.profile.dto; +package com.getpcpanel.integration.volume.overlay; /** * Position options for the on-screen overlay. diff --git a/src/main/java/com/getpcpanel/overlay/OverlayPreviewRenderer.java b/src/main/java/com/getpcpanel/integration/volume/overlay/OverlayPreviewRenderer.java similarity index 98% rename from src/main/java/com/getpcpanel/overlay/OverlayPreviewRenderer.java rename to src/main/java/com/getpcpanel/integration/volume/overlay/OverlayPreviewRenderer.java index 09af2083..db3e346f 100644 --- a/src/main/java/com/getpcpanel/overlay/OverlayPreviewRenderer.java +++ b/src/main/java/com/getpcpanel/integration/volume/overlay/OverlayPreviewRenderer.java @@ -1,4 +1,4 @@ -package com.getpcpanel.overlay; +package com.getpcpanel.integration.volume.overlay; import java.awt.Color; import java.awt.image.BufferedImage; diff --git a/src/main/java/com/getpcpanel/overlay/OverlayRenderer.java b/src/main/java/com/getpcpanel/integration/volume/overlay/OverlayRenderer.java similarity index 99% rename from src/main/java/com/getpcpanel/overlay/OverlayRenderer.java rename to src/main/java/com/getpcpanel/integration/volume/overlay/OverlayRenderer.java index b1761f8a..aed0c63a 100644 --- a/src/main/java/com/getpcpanel/overlay/OverlayRenderer.java +++ b/src/main/java/com/getpcpanel/integration/volume/overlay/OverlayRenderer.java @@ -1,4 +1,4 @@ -package com.getpcpanel.overlay; +package com.getpcpanel.integration.volume.overlay; import java.awt.Color; import java.awt.Font; diff --git a/src/main/java/com/getpcpanel/overlay/OverlayWindow.java b/src/main/java/com/getpcpanel/integration/volume/overlay/OverlayWindow.java similarity index 95% rename from src/main/java/com/getpcpanel/overlay/OverlayWindow.java rename to src/main/java/com/getpcpanel/integration/volume/overlay/OverlayWindow.java index 1a052bf5..f9718d4f 100644 --- a/src/main/java/com/getpcpanel/overlay/OverlayWindow.java +++ b/src/main/java/com/getpcpanel/integration/volume/overlay/OverlayWindow.java @@ -1,4 +1,4 @@ -package com.getpcpanel.overlay; +package com.getpcpanel.integration.volume.overlay; import com.getpcpanel.profile.Save; diff --git a/src/main/java/com/getpcpanel/overlay/ScreenSize.java b/src/main/java/com/getpcpanel/integration/volume/overlay/ScreenSize.java similarity index 87% rename from src/main/java/com/getpcpanel/overlay/ScreenSize.java rename to src/main/java/com/getpcpanel/integration/volume/overlay/ScreenSize.java index fdce8458..66a0b061 100644 --- a/src/main/java/com/getpcpanel/overlay/ScreenSize.java +++ b/src/main/java/com/getpcpanel/integration/volume/overlay/ScreenSize.java @@ -1,4 +1,4 @@ -package com.getpcpanel.overlay; +package com.getpcpanel.integration.volume.overlay; /** * The size of the primary screen, used to position the overlay. A plain value type so the overlay diff --git a/src/main/java/com/getpcpanel/overlay/Win32VolumeOverlay.java b/src/main/java/com/getpcpanel/integration/volume/overlay/Win32VolumeOverlay.java similarity index 99% rename from src/main/java/com/getpcpanel/overlay/Win32VolumeOverlay.java rename to src/main/java/com/getpcpanel/integration/volume/overlay/Win32VolumeOverlay.java index 3baa5f80..0a41ed83 100644 --- a/src/main/java/com/getpcpanel/overlay/Win32VolumeOverlay.java +++ b/src/main/java/com/getpcpanel/integration/volume/overlay/Win32VolumeOverlay.java @@ -1,4 +1,4 @@ -package com.getpcpanel.overlay; +package com.getpcpanel.integration.volume.overlay; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; @@ -51,7 +51,7 @@ * The thread parks while the overlay is hidden and is unparked by {@link #show} / the dismiss timer. */ @Log4j2 -public class Win32VolumeOverlay implements OverlayWindow { +class Win32VolumeOverlay implements OverlayWindow { /** Built-in window class with a native window procedure (avoids a Java callback). */ private static final String WINDOW_CLASS = "STATIC"; private static final int DISMISS_MS = 2000; diff --git a/src/main/java/com/getpcpanel/cpp/AudioDevice.java b/src/main/java/com/getpcpanel/integration/volume/platform/AudioDevice.java similarity index 96% rename from src/main/java/com/getpcpanel/cpp/AudioDevice.java rename to src/main/java/com/getpcpanel/integration/volume/platform/AudioDevice.java index 46e24deb..6a63817a 100644 --- a/src/main/java/com/getpcpanel/cpp/AudioDevice.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/AudioDevice.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp; +package com.getpcpanel.integration.volume.platform; import java.io.Serializable; diff --git a/src/main/java/com/getpcpanel/cpp/AudioDeviceEvent.java b/src/main/java/com/getpcpanel/integration/volume/platform/AudioDeviceEvent.java similarity index 59% rename from src/main/java/com/getpcpanel/cpp/AudioDeviceEvent.java rename to src/main/java/com/getpcpanel/integration/volume/platform/AudioDeviceEvent.java index 0e4bd372..446fabe8 100644 --- a/src/main/java/com/getpcpanel/cpp/AudioDeviceEvent.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/AudioDeviceEvent.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp; +package com.getpcpanel.integration.volume.platform; public record AudioDeviceEvent(AudioDevice device, EventType eventType) { } diff --git a/src/main/java/com/getpcpanel/cpp/AudioSession.java b/src/main/java/com/getpcpanel/integration/volume/platform/AudioSession.java similarity index 97% rename from src/main/java/com/getpcpanel/cpp/AudioSession.java rename to src/main/java/com/getpcpanel/integration/volume/platform/AudioSession.java index 74ebd316..6c1bd86d 100644 --- a/src/main/java/com/getpcpanel/cpp/AudioSession.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/AudioSession.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp; +package com.getpcpanel.integration.volume.platform; import java.io.File; diff --git a/src/main/java/com/getpcpanel/cpp/AudioSessionEvent.java b/src/main/java/com/getpcpanel/integration/volume/platform/AudioSessionEvent.java similarity index 60% rename from src/main/java/com/getpcpanel/cpp/AudioSessionEvent.java rename to src/main/java/com/getpcpanel/integration/volume/platform/AudioSessionEvent.java index b0120fab..9dd0d3d4 100644 --- a/src/main/java/com/getpcpanel/cpp/AudioSessionEvent.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/AudioSessionEvent.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp; +package com.getpcpanel.integration.volume.platform; public record AudioSessionEvent(AudioSession session, EventType eventType) { diff --git a/src/main/java/com/getpcpanel/cpp/DataFlow.java b/src/main/java/com/getpcpanel/integration/volume/platform/DataFlow.java similarity index 86% rename from src/main/java/com/getpcpanel/cpp/DataFlow.java rename to src/main/java/com/getpcpanel/integration/volume/platform/DataFlow.java index d15c6705..f74e769b 100644 --- a/src/main/java/com/getpcpanel/cpp/DataFlow.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/DataFlow.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp; +package com.getpcpanel.integration.volume.platform; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/getpcpanel/cpp/EventType.java b/src/main/java/com/getpcpanel/integration/volume/platform/EventType.java similarity index 54% rename from src/main/java/com/getpcpanel/cpp/EventType.java rename to src/main/java/com/getpcpanel/integration/volume/platform/EventType.java index 31740ded..92e7c2f6 100644 --- a/src/main/java/com/getpcpanel/cpp/EventType.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/EventType.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp; +package com.getpcpanel.integration.volume.platform; public enum EventType { ADDED, diff --git a/src/main/java/com/getpcpanel/cpp/ISndCtrl.java b/src/main/java/com/getpcpanel/integration/volume/platform/ISndCtrl.java similarity index 97% rename from src/main/java/com/getpcpanel/cpp/ISndCtrl.java rename to src/main/java/com/getpcpanel/integration/volume/platform/ISndCtrl.java index 2b28cc1d..c00444ae 100644 --- a/src/main/java/com/getpcpanel/cpp/ISndCtrl.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/ISndCtrl.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp; +package com.getpcpanel.integration.volume.platform; import java.io.File; import java.util.Collection; diff --git a/src/main/java/com/getpcpanel/cpp/MuteType.java b/src/main/java/com/getpcpanel/integration/volume/platform/MuteType.java similarity index 88% rename from src/main/java/com/getpcpanel/cpp/MuteType.java rename to src/main/java/com/getpcpanel/integration/volume/platform/MuteType.java index b29583c8..966bf9ab 100644 --- a/src/main/java/com/getpcpanel/cpp/MuteType.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/MuteType.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp; +package com.getpcpanel.integration.volume.platform; import java.util.function.Function; diff --git a/src/main/java/com/getpcpanel/cpp/Role.java b/src/main/java/com/getpcpanel/integration/volume/platform/Role.java similarity index 82% rename from src/main/java/com/getpcpanel/cpp/Role.java rename to src/main/java/com/getpcpanel/integration/volume/platform/Role.java index 3acdf78a..51b81a86 100644 --- a/src/main/java/com/getpcpanel/cpp/Role.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/Role.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp; +package com.getpcpanel.integration.volume.platform; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioAudioDevice.java b/src/main/java/com/getpcpanel/integration/volume/platform/linux/PulseAudioAudioDevice.java similarity index 74% rename from src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioAudioDevice.java rename to src/main/java/com/getpcpanel/integration/volume/platform/linux/PulseAudioAudioDevice.java index b95ceee6..4c114831 100644 --- a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioAudioDevice.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/linux/PulseAudioAudioDevice.java @@ -1,14 +1,14 @@ -package com.getpcpanel.cpp.linux.pulseaudio; +package com.getpcpanel.integration.volume.platform.linux; import jakarta.enterprise.event.Event; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.DataFlow; +import com.getpcpanel.integration.volume.platform.AudioDevice; +import com.getpcpanel.integration.volume.platform.DataFlow; import lombok.Getter; @Getter -public class PulseAudioAudioDevice extends AudioDevice { +class PulseAudioAudioDevice extends AudioDevice { private final int index; private final boolean isDefault; private final boolean isOutput; diff --git a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioAudioSession.java b/src/main/java/com/getpcpanel/integration/volume/platform/linux/PulseAudioAudioSession.java similarity index 86% rename from src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioAudioSession.java rename to src/main/java/com/getpcpanel/integration/volume/platform/linux/PulseAudioAudioSession.java index 93dfec28..8eb1ceef 100644 --- a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioAudioSession.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/linux/PulseAudioAudioSession.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.linux.pulseaudio; +package com.getpcpanel.integration.volume.platform.linux; import java.io.File; @@ -6,14 +6,14 @@ import jakarta.enterprise.event.Event; -import com.getpcpanel.cpp.AudioSession; +import com.getpcpanel.integration.volume.platform.AudioSession; import lombok.EqualsAndHashCode; import lombok.Getter; @Getter @EqualsAndHashCode(callSuper = true) -public class PulseAudioAudioSession extends AudioSession { +class PulseAudioAudioSession extends AudioSession { private final int index; /** diff --git a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioEventListener.java b/src/main/java/com/getpcpanel/integration/volume/platform/linux/PulseAudioEventListener.java similarity index 95% rename from src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioEventListener.java rename to src/main/java/com/getpcpanel/integration/volume/platform/linux/PulseAudioEventListener.java index 2f15c48c..754b403c 100644 --- a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioEventListener.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/linux/PulseAudioEventListener.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.linux.pulseaudio; +package com.getpcpanel.integration.volume.platform.linux; import java.io.BufferedReader; import java.io.IOException; @@ -14,7 +14,7 @@ import javax.annotation.Nullable; import com.getpcpanel.platform.LinuxBuild; -import com.getpcpanel.util.ProcessHelper; +import com.getpcpanel.util.os.ProcessHelper; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; @@ -28,7 +28,7 @@ @Startup @Singleton @LinuxBuild -public class PulseAudioEventListener { +class PulseAudioEventListener { @Inject Event eventBus; @Inject diff --git a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioWrapper.java b/src/main/java/com/getpcpanel/integration/volume/platform/linux/PulseAudioWrapper.java similarity index 97% rename from src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioWrapper.java rename to src/main/java/com/getpcpanel/integration/volume/platform/linux/PulseAudioWrapper.java index 9e138ef9..a9a12930 100644 --- a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioWrapper.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/linux/PulseAudioWrapper.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.linux.pulseaudio; +package com.getpcpanel.integration.volume.platform.linux; import java.io.IOException; import java.nio.charset.Charset; @@ -16,9 +16,9 @@ import jakarta.inject.Inject; import jakarta.enterprise.context.ApplicationScoped; -import com.getpcpanel.cpp.MuteType; +import com.getpcpanel.integration.volume.platform.MuteType; import com.getpcpanel.platform.LinuxBuild; -import com.getpcpanel.util.ProcessHelper; +import com.getpcpanel.util.os.ProcessHelper; import lombok.Builder; import lombok.extern.log4j.Log4j2; @@ -27,7 +27,7 @@ @Log4j2 @ApplicationScoped @LinuxBuild -public class PulseAudioWrapper { +class PulseAudioWrapper { public static final int NO_OP_IDX = -1; public static final int DEFAULT_DEVICE = -2; private static final Pattern pactlFirstLine = Pattern.compile("(.*) #(\\d+)"); diff --git a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/SndCtrlPulseAudio.java b/src/main/java/com/getpcpanel/integration/volume/platform/linux/SndCtrlPulseAudio.java similarity index 92% rename from src/main/java/com/getpcpanel/cpp/linux/pulseaudio/SndCtrlPulseAudio.java rename to src/main/java/com/getpcpanel/integration/volume/platform/linux/SndCtrlPulseAudio.java index e752c225..eeb444e2 100644 --- a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/SndCtrlPulseAudio.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/linux/SndCtrlPulseAudio.java @@ -1,7 +1,7 @@ -package com.getpcpanel.cpp.linux.pulseaudio; +package com.getpcpanel.integration.volume.platform.linux; -import static com.getpcpanel.cpp.linux.pulseaudio.PulseAudioWrapper.volumeFtoI; -import static com.getpcpanel.cpp.linux.pulseaudio.PulseAudioWrapper.volumeItoF; +import static com.getpcpanel.integration.volume.platform.linux.PulseAudioWrapper.volumeFtoI; +import static com.getpcpanel.integration.volume.platform.linux.PulseAudioWrapper.volumeItoF; import java.io.File; import java.util.Collection; @@ -20,18 +20,18 @@ import javax.annotation.Nullable; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.AudioSession; -import com.getpcpanel.cpp.AudioSessionEvent; -import com.getpcpanel.cpp.EventType; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.cpp.MuteType; -import com.getpcpanel.cpp.linux.LinuxProcessHelper; -import com.getpcpanel.cpp.linux.LinuxProcessHelper.ActiveWindow; -import com.getpcpanel.cpp.linux.pulseaudio.PulseAudioEventListener.LinuxDeviceChangedEvent; -import com.getpcpanel.cpp.linux.pulseaudio.PulseAudioEventListener.LinuxSessionChangedEvent; -import com.getpcpanel.cpp.linux.pulseaudio.PulseAudioWrapper.InOutput; -import com.getpcpanel.cpp.linux.pulseaudio.PulseAudioWrapper.PulseAudioTarget; +import com.getpcpanel.integration.volume.platform.AudioDevice; +import com.getpcpanel.integration.volume.platform.AudioSession; +import com.getpcpanel.integration.volume.platform.AudioSessionEvent; +import com.getpcpanel.integration.volume.platform.EventType; +import com.getpcpanel.integration.volume.platform.ISndCtrl; +import com.getpcpanel.integration.volume.platform.MuteType; +import com.getpcpanel.platform.process.LinuxProcessHelper; +import com.getpcpanel.platform.process.LinuxProcessHelper.ActiveWindow; +import com.getpcpanel.integration.volume.platform.linux.PulseAudioEventListener.LinuxDeviceChangedEvent; +import com.getpcpanel.integration.volume.platform.linux.PulseAudioEventListener.LinuxSessionChangedEvent; +import com.getpcpanel.integration.volume.platform.linux.PulseAudioWrapper.InOutput; +import com.getpcpanel.integration.volume.platform.linux.PulseAudioWrapper.PulseAudioTarget; import com.getpcpanel.platform.LinuxBuild; import io.quarkus.runtime.StartupEvent; @@ -46,7 +46,7 @@ @Log4j2 @ApplicationScoped @LinuxBuild -public class SndCtrlPulseAudio implements ISndCtrl { +class SndCtrlPulseAudio implements ISndCtrl { public static final String INPUT_PREFIX = "in_"; @Inject PulseAudioWrapper cmd; @Inject LinuxProcessHelper processHelper; diff --git a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/SndCtrlPulseAudioDebug.java b/src/main/java/com/getpcpanel/integration/volume/platform/linux/SndCtrlPulseAudioDebug.java similarity index 94% rename from src/main/java/com/getpcpanel/cpp/linux/pulseaudio/SndCtrlPulseAudioDebug.java rename to src/main/java/com/getpcpanel/integration/volume/platform/linux/SndCtrlPulseAudioDebug.java index 802c8a13..ac40179c 100644 --- a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/SndCtrlPulseAudioDebug.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/linux/SndCtrlPulseAudioDebug.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.linux.pulseaudio; +package com.getpcpanel.integration.volume.platform.linux; import java.io.OutputStream; import java.nio.charset.StandardCharsets; @@ -13,7 +13,7 @@ @Log4j2 @ApplicationScoped @LinuxBuild -public class SndCtrlPulseAudioDebug { +class SndCtrlPulseAudioDebug { /** Clipboard helpers tried in order: Wayland first, then the two common X11 tools. */ private static final String[][] CLIPBOARD_COMMANDS = { { "wl-copy" }, diff --git a/src/main/java/com/getpcpanel/cpp/lombok.config b/src/main/java/com/getpcpanel/integration/volume/platform/lombok.config similarity index 100% rename from src/main/java/com/getpcpanel/cpp/lombok.config rename to src/main/java/com/getpcpanel/integration/volume/platform/lombok.config diff --git a/src/main/java/com/getpcpanel/cpp/osx/CoreAudioLib.java b/src/main/java/com/getpcpanel/integration/volume/platform/osx/CoreAudioLib.java similarity index 97% rename from src/main/java/com/getpcpanel/cpp/osx/CoreAudioLib.java rename to src/main/java/com/getpcpanel/integration/volume/platform/osx/CoreAudioLib.java index a3d9bd74..774c7162 100644 --- a/src/main/java/com/getpcpanel/cpp/osx/CoreAudioLib.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/osx/CoreAudioLib.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.osx; +package com.getpcpanel.integration.volume.platform.osx; import com.sun.jna.Callback; import com.sun.jna.Library; diff --git a/src/main/java/com/getpcpanel/cpp/osx/CoreAudioWrapper.java b/src/main/java/com/getpcpanel/integration/volume/platform/osx/CoreAudioWrapper.java similarity index 99% rename from src/main/java/com/getpcpanel/cpp/osx/CoreAudioWrapper.java rename to src/main/java/com/getpcpanel/integration/volume/platform/osx/CoreAudioWrapper.java index f0ef2050..1cc4dbd0 100644 --- a/src/main/java/com/getpcpanel/cpp/osx/CoreAudioWrapper.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/osx/CoreAudioWrapper.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.osx; +package com.getpcpanel.integration.volume.platform.osx; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -20,7 +20,7 @@ import lombok.extern.log4j.Log4j2; /** - * Friendly wrapper around the CoreAudio HAL. All volumes are scalar 0..1, matching {@link com.getpcpanel.cpp.ISndCtrl}. + * Friendly wrapper around the CoreAudio HAL. All volumes are scalar 0..1, matching {@link com.getpcpanel.integration.volume.platform.ISndCtrl}. */ @Log4j2 @ApplicationScoped diff --git a/src/main/java/com/getpcpanel/cpp/osx/OsxAudioDevice.java b/src/main/java/com/getpcpanel/integration/volume/platform/osx/OsxAudioDevice.java similarity index 71% rename from src/main/java/com/getpcpanel/cpp/osx/OsxAudioDevice.java rename to src/main/java/com/getpcpanel/integration/volume/platform/osx/OsxAudioDevice.java index 3785b3c1..1a19233b 100644 --- a/src/main/java/com/getpcpanel/cpp/osx/OsxAudioDevice.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/osx/OsxAudioDevice.java @@ -1,16 +1,16 @@ -package com.getpcpanel.cpp.osx; +package com.getpcpanel.integration.volume.platform.osx; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.AudioDeviceEvent; -import com.getpcpanel.cpp.DataFlow; -import com.getpcpanel.cpp.EventType; -import com.getpcpanel.cpp.osx.CoreAudioWrapper.CoreAudioDevice; +import com.getpcpanel.integration.volume.platform.AudioDevice; +import com.getpcpanel.integration.volume.platform.AudioDeviceEvent; +import com.getpcpanel.integration.volume.platform.DataFlow; +import com.getpcpanel.integration.volume.platform.EventType; +import com.getpcpanel.integration.volume.platform.osx.CoreAudioWrapper.CoreAudioDevice; import jakarta.enterprise.event.Event; import lombok.Getter; @Getter -public class OsxAudioDevice extends AudioDevice { +class OsxAudioDevice extends AudioDevice { private final int deviceId; private final boolean isDefault; diff --git a/src/main/java/com/getpcpanel/cpp/osx/SndCtrlOsx.java b/src/main/java/com/getpcpanel/integration/volume/platform/osx/SndCtrlOsx.java similarity index 88% rename from src/main/java/com/getpcpanel/cpp/osx/SndCtrlOsx.java rename to src/main/java/com/getpcpanel/integration/volume/platform/osx/SndCtrlOsx.java index a57712a8..fb5ba16e 100644 --- a/src/main/java/com/getpcpanel/cpp/osx/SndCtrlOsx.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/osx/SndCtrlOsx.java @@ -1,14 +1,14 @@ -package com.getpcpanel.cpp.osx; +package com.getpcpanel.integration.volume.platform.osx; -import static com.getpcpanel.cpp.osx.CoreAudioWrapper.PROP_DEFAULT_INPUT_DEVICE; -import static com.getpcpanel.cpp.osx.CoreAudioWrapper.PROP_DEFAULT_OUTPUT_DEVICE; -import static com.getpcpanel.cpp.osx.CoreAudioWrapper.PROP_DEVICES; -import static com.getpcpanel.cpp.osx.CoreAudioWrapper.PROP_MUTE; -import static com.getpcpanel.cpp.osx.CoreAudioWrapper.PROP_VOLUME_SCALAR; -import static com.getpcpanel.cpp.osx.CoreAudioWrapper.SCOPE_GLOBAL; -import static com.getpcpanel.cpp.osx.CoreAudioWrapper.SCOPE_INPUT; -import static com.getpcpanel.cpp.osx.CoreAudioWrapper.SCOPE_OUTPUT; -import static com.getpcpanel.cpp.osx.CoreAudioWrapper.SCOPE_WILDCARD; +import static com.getpcpanel.integration.volume.platform.osx.CoreAudioWrapper.PROP_DEFAULT_INPUT_DEVICE; +import static com.getpcpanel.integration.volume.platform.osx.CoreAudioWrapper.PROP_DEFAULT_OUTPUT_DEVICE; +import static com.getpcpanel.integration.volume.platform.osx.CoreAudioWrapper.PROP_DEVICES; +import static com.getpcpanel.integration.volume.platform.osx.CoreAudioWrapper.PROP_MUTE; +import static com.getpcpanel.integration.volume.platform.osx.CoreAudioWrapper.PROP_VOLUME_SCALAR; +import static com.getpcpanel.integration.volume.platform.osx.CoreAudioWrapper.SCOPE_GLOBAL; +import static com.getpcpanel.integration.volume.platform.osx.CoreAudioWrapper.SCOPE_INPUT; +import static com.getpcpanel.integration.volume.platform.osx.CoreAudioWrapper.SCOPE_OUTPUT; +import static com.getpcpanel.integration.volume.platform.osx.CoreAudioWrapper.SCOPE_WILDCARD; import java.io.File; import java.util.ArrayList; @@ -26,13 +26,14 @@ import org.apache.commons.lang3.StringUtils; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.AudioDeviceEvent; -import com.getpcpanel.cpp.AudioSession; -import com.getpcpanel.cpp.EventType; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.cpp.MuteType; +import com.getpcpanel.integration.volume.platform.AudioDevice; +import com.getpcpanel.integration.volume.platform.AudioDeviceEvent; +import com.getpcpanel.integration.volume.platform.AudioSession; +import com.getpcpanel.integration.volume.platform.EventType; +import com.getpcpanel.integration.volume.platform.ISndCtrl; +import com.getpcpanel.integration.volume.platform.MuteType; import com.getpcpanel.platform.MacBuild; +import com.getpcpanel.platform.process.OsxProcessHelper; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; @@ -51,7 +52,7 @@ @ApplicationScoped @MacBuild @RequiredArgsConstructor -public class SndCtrlOsx implements ISndCtrl { +class SndCtrlOsx implements ISndCtrl { private static final String DEFAULT = "default"; private static final List SYSTEM_APP_PREFIXES = List.of("/System/Library/", "/System/Cryptexes/", "/usr/libexec/"); private static final Comparator APP_ORDER = Comparator.comparing(RunningApplication::name, String.CASE_INSENSITIVE_ORDER); diff --git a/src/main/java/com/getpcpanel/cpp/windows/ProcessHelper.java b/src/main/java/com/getpcpanel/integration/volume/platform/windows/ProcessHelper.java similarity index 91% rename from src/main/java/com/getpcpanel/cpp/windows/ProcessHelper.java rename to src/main/java/com/getpcpanel/integration/volume/platform/windows/ProcessHelper.java index 09f7cfe8..7311aeb4 100644 --- a/src/main/java/com/getpcpanel/cpp/windows/ProcessHelper.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/windows/ProcessHelper.java @@ -1,10 +1,10 @@ -package com.getpcpanel.cpp.windows; +package com.getpcpanel.integration.volume.platform.windows; import java.io.File; import java.util.Comparator; import java.util.List; -import com.getpcpanel.cpp.ISndCtrl; +import com.getpcpanel.integration.volume.platform.ISndCtrl; import com.sun.jna.platform.win32.Kernel32; import com.sun.jna.platform.win32.PsapiUtil; import com.sun.jna.platform.win32.WinDef; diff --git a/src/main/java/com/getpcpanel/cpp/windows/SndCtrlNative.java b/src/main/java/com/getpcpanel/integration/volume/platform/windows/SndCtrlNative.java similarity index 94% rename from src/main/java/com/getpcpanel/cpp/windows/SndCtrlNative.java rename to src/main/java/com/getpcpanel/integration/volume/platform/windows/SndCtrlNative.java index c0f0a2d6..f4d88225 100644 --- a/src/main/java/com/getpcpanel/cpp/windows/SndCtrlNative.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/windows/SndCtrlNative.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.windows; +package com.getpcpanel.integration.volume.platform.windows; import javax.annotation.Nullable; diff --git a/src/main/java/com/getpcpanel/cpp/windows/SndCtrlWindows.java b/src/main/java/com/getpcpanel/integration/volume/platform/windows/SndCtrlWindows.java similarity index 93% rename from src/main/java/com/getpcpanel/cpp/windows/SndCtrlWindows.java rename to src/main/java/com/getpcpanel/integration/volume/platform/windows/SndCtrlWindows.java index ff3254e2..dfe4324b 100644 --- a/src/main/java/com/getpcpanel/cpp/windows/SndCtrlWindows.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/windows/SndCtrlWindows.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.windows; +package com.getpcpanel.integration.volume.platform.windows; import java.util.Collection; import java.util.Collections; @@ -13,16 +13,17 @@ import org.apache.commons.lang3.StringUtils; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.AudioDeviceEvent; -import com.getpcpanel.cpp.AudioSession; -import com.getpcpanel.cpp.DataFlow; -import com.getpcpanel.cpp.EventType; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.cpp.MuteType; -import com.getpcpanel.cpp.Role; +import com.getpcpanel.integration.volume.platform.AudioDevice; +import com.getpcpanel.integration.volume.platform.AudioDeviceEvent; +import com.getpcpanel.integration.volume.platform.AudioSession; +import com.getpcpanel.integration.volume.platform.DataFlow; +import com.getpcpanel.integration.volume.platform.EventType; +import com.getpcpanel.integration.volume.platform.ISndCtrl; +import com.getpcpanel.integration.volume.platform.MuteType; +import com.getpcpanel.integration.volume.platform.Role; import com.getpcpanel.platform.WindowsBuild; -import com.getpcpanel.util.ExtractUtil; +import com.getpcpanel.profile.WindowFocusChangedEvent; +import com.getpcpanel.util.io.ExtractUtil; import jakarta.annotation.PostConstruct; import jakarta.enterprise.context.ApplicationScoped; diff --git a/src/main/java/com/getpcpanel/cpp/windows/WindowsAudioDevice.java b/src/main/java/com/getpcpanel/integration/volume/platform/windows/WindowsAudioDevice.java similarity index 83% rename from src/main/java/com/getpcpanel/cpp/windows/WindowsAudioDevice.java rename to src/main/java/com/getpcpanel/integration/volume/platform/windows/WindowsAudioDevice.java index b89f67b8..1f74cb45 100644 --- a/src/main/java/com/getpcpanel/cpp/windows/WindowsAudioDevice.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/windows/WindowsAudioDevice.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.windows; +package com.getpcpanel.integration.volume.platform.windows; import java.io.File; import java.util.HashMap; @@ -8,17 +8,17 @@ import com.fasterxml.jackson.annotation.JsonIgnore; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.AudioSession; -import com.getpcpanel.cpp.AudioSessionEvent; -import com.getpcpanel.cpp.DataFlow; -import com.getpcpanel.cpp.EventType; +import com.getpcpanel.integration.volume.platform.AudioDevice; +import com.getpcpanel.integration.volume.platform.AudioSession; +import com.getpcpanel.integration.volume.platform.AudioSessionEvent; +import com.getpcpanel.integration.volume.platform.DataFlow; +import com.getpcpanel.integration.volume.platform.EventType; import lombok.extern.log4j.Log4j2; @Log4j2 @SuppressWarnings("unused") // Methods called from JNI -public class WindowsAudioDevice extends AudioDevice { +class WindowsAudioDevice extends AudioDevice { private final transient Map sessions = new HashMap<>(); // pid -> pointer_addr -> session public WindowsAudioDevice(Event eventBus, String name, String id) { diff --git a/src/main/java/com/getpcpanel/cpp/windows/WindowsAudioSession.java b/src/main/java/com/getpcpanel/integration/volume/platform/windows/WindowsAudioSession.java similarity index 74% rename from src/main/java/com/getpcpanel/cpp/windows/WindowsAudioSession.java rename to src/main/java/com/getpcpanel/integration/volume/platform/windows/WindowsAudioSession.java index ef18ee4b..062a20cb 100644 --- a/src/main/java/com/getpcpanel/cpp/windows/WindowsAudioSession.java +++ b/src/main/java/com/getpcpanel/integration/volume/platform/windows/WindowsAudioSession.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.windows; +package com.getpcpanel.integration.volume.platform.windows; import java.io.File; import java.util.HashSet; @@ -8,8 +8,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.AudioSession; +import com.getpcpanel.integration.volume.platform.AudioDevice; +import com.getpcpanel.integration.volume.platform.AudioSession; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -17,7 +17,7 @@ @Getter @EqualsAndHashCode(callSuper = true) -public class WindowsAudioSession extends AudioSession { +class WindowsAudioSession extends AudioSession { @JsonIgnore @ToString.Exclude private final AudioDevice device; @JsonIgnore private final Set pointers = new HashSet<>(); diff --git a/src/main/java/com/getpcpanel/wavelink/WaveLinkAppCache.java b/src/main/java/com/getpcpanel/integration/wavelink/WaveLinkAppCache.java similarity index 99% rename from src/main/java/com/getpcpanel/wavelink/WaveLinkAppCache.java rename to src/main/java/com/getpcpanel/integration/wavelink/WaveLinkAppCache.java index 0eab1961..7dc5d8fe 100644 --- a/src/main/java/com/getpcpanel/wavelink/WaveLinkAppCache.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/WaveLinkAppCache.java @@ -1,4 +1,4 @@ -package com.getpcpanel.wavelink; +package com.getpcpanel.integration.wavelink; import java.nio.file.Files; import java.nio.file.Path; @@ -13,7 +13,7 @@ import java.util.concurrent.ConcurrentHashMap; import com.fasterxml.jackson.databind.ObjectMapper; -import com.getpcpanel.util.FileUtil; +import com.getpcpanel.util.io.FileUtil; import dev.niels.wavelink.impl.model.WaveLinkApp; import dev.niels.wavelink.impl.model.WaveLinkChannel; diff --git a/src/main/java/com/getpcpanel/wavelink/WaveLinkChangedEvent.java b/src/main/java/com/getpcpanel/integration/wavelink/WaveLinkChangedEvent.java similarity index 87% rename from src/main/java/com/getpcpanel/wavelink/WaveLinkChangedEvent.java rename to src/main/java/com/getpcpanel/integration/wavelink/WaveLinkChangedEvent.java index 8165a1ce..39d337e2 100644 --- a/src/main/java/com/getpcpanel/wavelink/WaveLinkChangedEvent.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/WaveLinkChangedEvent.java @@ -1,4 +1,4 @@ -package com.getpcpanel.wavelink; +package com.getpcpanel.integration.wavelink; /** * Fired whenever Wave Link reports a state change (a channel/mix/output changed, incl. its mute state). diff --git a/src/main/java/com/getpcpanel/wavelink/WaveLinkIconHandler.java b/src/main/java/com/getpcpanel/integration/wavelink/WaveLinkIconHandler.java similarity index 79% rename from src/main/java/com/getpcpanel/wavelink/WaveLinkIconHandler.java rename to src/main/java/com/getpcpanel/integration/wavelink/WaveLinkIconHandler.java index e5359e23..cbbb642d 100644 --- a/src/main/java/com/getpcpanel/wavelink/WaveLinkIconHandler.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/WaveLinkIconHandler.java @@ -1,4 +1,4 @@ -package com.getpcpanel.wavelink; +package com.getpcpanel.integration.wavelink; import java.awt.image.BufferedImage; import java.util.Optional; @@ -7,11 +7,11 @@ import com.getpcpanel.commands.IIconHandler; import com.getpcpanel.commands.IconService; -import com.getpcpanel.wavelink.command.CommandWaveLink; -import com.getpcpanel.wavelink.command.CommandWaveLinkAddFocusToChannel; -import com.getpcpanel.wavelink.command.CommandWaveLinkChange; -import com.getpcpanel.wavelink.command.CommandWaveLinkChannelEffect; -import com.getpcpanel.wavelink.command.CommandWaveLinkMainOutput; +import com.getpcpanel.integration.wavelink.command.CommandWaveLink; +import com.getpcpanel.integration.wavelink.command.CommandWaveLinkAddFocusToChannel; +import com.getpcpanel.integration.wavelink.command.CommandWaveLinkChange; +import com.getpcpanel.integration.wavelink.command.CommandWaveLinkChannelEffect; +import com.getpcpanel.integration.wavelink.command.CommandWaveLinkMainOutput; import dev.niels.wavelink.impl.model.WaveLinkChannel; import dev.niels.wavelink.impl.model.WaveLinkImage; @@ -21,7 +21,7 @@ @Log4j2 @ApplicationScoped -public class WaveLinkIconHandler implements IIconHandler { +class WaveLinkIconHandler implements IIconHandler { @Inject WaveLinkService waveLinkService; @Inject diff --git a/src/main/java/com/getpcpanel/mutecolor/WaveLinkMuteResolver.java b/src/main/java/com/getpcpanel/integration/wavelink/WaveLinkMuteResolver.java similarity index 90% rename from src/main/java/com/getpcpanel/mutecolor/WaveLinkMuteResolver.java rename to src/main/java/com/getpcpanel/integration/wavelink/WaveLinkMuteResolver.java index 5b608fc4..0c594bbf 100644 --- a/src/main/java/com/getpcpanel/mutecolor/WaveLinkMuteResolver.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/WaveLinkMuteResolver.java @@ -1,12 +1,13 @@ -package com.getpcpanel.mutecolor; +package com.getpcpanel.integration.wavelink; +import com.getpcpanel.integration.volume.mutecolor.MuteStateResolver; import java.util.Optional; import org.apache.commons.lang3.StringUtils; import com.getpcpanel.commands.Commands; -import com.getpcpanel.wavelink.WaveLinkService; -import com.getpcpanel.wavelink.command.CommandWaveLinkChange; +import com.getpcpanel.integration.wavelink.WaveLinkService; +import com.getpcpanel.integration.wavelink.command.CommandWaveLinkChange; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; @@ -18,7 +19,7 @@ * mute-override colour. */ @ApplicationScoped -public class WaveLinkMuteResolver implements MuteStateResolver { +class WaveLinkMuteResolver implements MuteStateResolver { private final WaveLinkService waveLink; @Inject diff --git a/src/main/java/com/getpcpanel/wavelink/WaveLinkService.java b/src/main/java/com/getpcpanel/integration/wavelink/WaveLinkService.java similarity index 98% rename from src/main/java/com/getpcpanel/wavelink/WaveLinkService.java rename to src/main/java/com/getpcpanel/integration/wavelink/WaveLinkService.java index ac25b9cd..b1661939 100644 --- a/src/main/java/com/getpcpanel/wavelink/WaveLinkService.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/WaveLinkService.java @@ -1,4 +1,4 @@ -package com.getpcpanel.wavelink; +package com.getpcpanel.integration.wavelink; import java.net.ConnectException; import java.util.Map; @@ -11,13 +11,13 @@ import javax.annotation.Nullable; -import com.getpcpanel.cpp.AudioSession; -import com.getpcpanel.cpp.ISndCtrl; +import com.getpcpanel.integration.volume.platform.AudioSession; +import com.getpcpanel.integration.volume.platform.ISndCtrl; import com.getpcpanel.profile.SaveService; import com.getpcpanel.profile.SaveService.SaveEvent; -import com.getpcpanel.util.Debouncer; -import com.getpcpanel.util.ReconnectBackoff; -import com.getpcpanel.volume.IFocusRedirector; +import com.getpcpanel.util.concurrent.Debouncer; +import com.getpcpanel.util.concurrent.ReconnectBackoff; +import com.getpcpanel.integration.volume.IFocusRedirector; import jakarta.enterprise.inject.Instance; diff --git a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLink.java b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLink.java similarity index 80% rename from src/main/java/com/getpcpanel/wavelink/command/CommandWaveLink.java rename to src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLink.java index 9ff80e7f..026ff059 100644 --- a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLink.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLink.java @@ -1,8 +1,8 @@ -package com.getpcpanel.wavelink.command; +package com.getpcpanel.integration.wavelink.command; import com.getpcpanel.util.CdiHelper; import com.getpcpanel.commands.command.Command; -import com.getpcpanel.wavelink.WaveLinkService; +import com.getpcpanel.integration.wavelink.WaveLinkService; import lombok.Getter; import lombok.ToString; diff --git a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkAddFocusToChannel.java b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkAddFocusToChannel.java similarity index 67% rename from src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkAddFocusToChannel.java rename to src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkAddFocusToChannel.java index f8832f3b..d9d5d38d 100644 --- a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkAddFocusToChannel.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkAddFocusToChannel.java @@ -1,10 +1,14 @@ -package com.getpcpanel.wavelink.command; +package com.getpcpanel.integration.wavelink.command; import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.command.ButtonAction; @@ -15,6 +19,8 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("wavelink.add-focus-to-channel") +@CommandMeta(label = "Wave Link — add focused app", category = CommandCategory.integration, kinds = {CommandKind.button}, integration = "wavelink", icon = "plus", legacyIds = {"com.getpcpanel.wavelink.command.CommandWaveLinkAddFocusToChannel"}) public final class CommandWaveLinkAddFocusToChannel extends CommandWaveLink implements ButtonAction { @Nullable private final String id; @Nullable private final String name; diff --git a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChange.java b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChange.java similarity index 95% rename from src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChange.java rename to src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChange.java index 5c4adee8..db5efd51 100644 --- a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChange.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChange.java @@ -1,4 +1,4 @@ -package com.getpcpanel.wavelink.command; +package com.getpcpanel.integration.wavelink.command; import javax.annotation.Nullable; diff --git a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChangeLevel.java b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChangeLevel.java similarity index 77% rename from src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChangeLevel.java rename to src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChangeLevel.java index f537a9f5..c83f0005 100644 --- a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChangeLevel.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChangeLevel.java @@ -1,10 +1,14 @@ -package com.getpcpanel.wavelink.command; +package com.getpcpanel.integration.wavelink.command; import org.apache.commons.lang3.StringUtils; import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.command.DialAction; @@ -15,6 +19,8 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("wavelink.change-level") +@CommandMeta(label = "Wave Link — level", category = CommandCategory.integration, kinds = {CommandKind.dial}, integration = "wavelink", icon = "sliders", legacyIds = {"com.getpcpanel.wavelink.command.CommandWaveLinkChangeLevel"}) public final class CommandWaveLinkChangeLevel extends CommandWaveLinkChange implements DialAction { @Nullable private final DialCommandParams dialParams; diff --git a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChangeMute.java b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChangeMute.java similarity index 84% rename from src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChangeMute.java rename to src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChangeMute.java index ab6b6691..5e247670 100644 --- a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChangeMute.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChangeMute.java @@ -1,13 +1,17 @@ -package com.getpcpanel.wavelink.command; +package com.getpcpanel.integration.wavelink.command; import org.apache.commons.lang3.StringUtils; import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.command.ButtonAction; -import com.getpcpanel.cpp.MuteType; +import com.getpcpanel.integration.volume.platform.MuteType; import dev.niels.wavelink.impl.model.WaveLinkInputDevice; import lombok.Getter; @@ -17,6 +21,8 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("wavelink.change-mute") +@CommandMeta(label = "Wave Link — mute", category = CommandCategory.integration, kinds = {CommandKind.button}, integration = "wavelink", icon = "mic-off", legacyIds = {"com.getpcpanel.wavelink.command.CommandWaveLinkChangeMute"}) public final class CommandWaveLinkChangeMute extends CommandWaveLinkChange implements ButtonAction { private final MuteType muteType; diff --git a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChannelEffect.java b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChannelEffect.java similarity index 90% rename from src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChannelEffect.java rename to src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChannelEffect.java index 394c151f..19d20f43 100644 --- a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChannelEffect.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChannelEffect.java @@ -1,4 +1,4 @@ -package com.getpcpanel.wavelink.command; +package com.getpcpanel.integration.wavelink.command; import java.util.List; import java.util.Objects; @@ -6,9 +6,10 @@ import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.command.ButtonAction; -import com.getpcpanel.cpp.MuteType; +import com.getpcpanel.integration.volume.platform.MuteType; import dev.niels.wavelink.impl.model.WaveLinkEffect; import lombok.Getter; @@ -19,6 +20,7 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("com.getpcpanel.wavelink.command.CommandWaveLinkChannelEffect") public final class CommandWaveLinkChannelEffect extends CommandWaveLink implements ButtonAction { @Nullable private final String channelId; @Nullable private final String channelName; diff --git a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkMainOutput.java b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkMainOutput.java similarity index 69% rename from src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkMainOutput.java rename to src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkMainOutput.java index 04079b64..fcab80ce 100644 --- a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkMainOutput.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkMainOutput.java @@ -1,10 +1,14 @@ -package com.getpcpanel.wavelink.command; +package com.getpcpanel.integration.wavelink.command; import org.apache.commons.lang3.StringUtils; import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.meta.CommandCategory; +import com.getpcpanel.commands.meta.CommandKind; +import com.getpcpanel.commands.meta.CommandMeta; import com.fasterxml.jackson.annotation.JsonProperty; import com.getpcpanel.commands.command.ButtonAction; @@ -15,6 +19,8 @@ @Getter @Log4j2 @ToString(callSuper = true) +@JsonTypeName("wavelink.main-output") +@CommandMeta(label = "Wave Link — main output", category = CommandCategory.integration, kinds = {CommandKind.button}, integration = "wavelink", icon = "volume", legacyIds = {"com.getpcpanel.wavelink.command.CommandWaveLinkMainOutput"}) public final class CommandWaveLinkMainOutput extends CommandWaveLink implements ButtonAction { @Nullable private final String id; @Nullable private final String name; diff --git a/src/main/java/com/getpcpanel/integration/wavelink/command/WaveLinkCommandModule.java b/src/main/java/com/getpcpanel/integration/wavelink/command/WaveLinkCommandModule.java new file mode 100644 index 00000000..595734a9 --- /dev/null +++ b/src/main/java/com/getpcpanel/integration/wavelink/command/WaveLinkCommandModule.java @@ -0,0 +1,25 @@ +package com.getpcpanel.integration.wavelink.command; + +import java.util.List; + +import com.getpcpanel.commands.CommandModule; +import com.getpcpanel.commands.command.Command; + +import jakarta.enterprise.context.ApplicationScoped; + +/** + * WaveLink feature module: registers its own command types via the + * {@link com.getpcpanel.commands.CommandModule} SPI. Adding/removing a command touches only this package. + */ +@ApplicationScoped +public class WaveLinkCommandModule implements CommandModule { + @Override + public List> commandTypes() { + return List.of( + CommandWaveLinkAddFocusToChannel.class, + CommandWaveLinkChangeLevel.class, + CommandWaveLinkChangeMute.class, + CommandWaveLinkChannelEffect.class, + CommandWaveLinkMainOutput.class); + } +} diff --git a/src/main/java/com/getpcpanel/wavelink/command/WaveLinkCommandTarget.java b/src/main/java/com/getpcpanel/integration/wavelink/command/WaveLinkCommandTarget.java similarity index 57% rename from src/main/java/com/getpcpanel/wavelink/command/WaveLinkCommandTarget.java rename to src/main/java/com/getpcpanel/integration/wavelink/command/WaveLinkCommandTarget.java index e2a02368..7b6113fa 100644 --- a/src/main/java/com/getpcpanel/wavelink/command/WaveLinkCommandTarget.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/command/WaveLinkCommandTarget.java @@ -1,4 +1,4 @@ -package com.getpcpanel.wavelink.command; +package com.getpcpanel.integration.wavelink.command; public enum WaveLinkCommandTarget { Input, Channel, Mix, Output diff --git a/src/main/java/com/getpcpanel/profile/dto/WaveLinkSettings.java b/src/main/java/com/getpcpanel/integration/wavelink/dto/WaveLinkSettings.java similarity index 97% rename from src/main/java/com/getpcpanel/profile/dto/WaveLinkSettings.java rename to src/main/java/com/getpcpanel/integration/wavelink/dto/WaveLinkSettings.java index 71263f32..59727a8f 100644 --- a/src/main/java/com/getpcpanel/profile/dto/WaveLinkSettings.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/dto/WaveLinkSettings.java @@ -1,4 +1,4 @@ -package com.getpcpanel.profile.dto; +package com.getpcpanel.integration.wavelink.dto; import javax.annotation.Nullable; diff --git a/src/main/java/com/getpcpanel/rest/wavelink/WaveLinkResource.java b/src/main/java/com/getpcpanel/integration/wavelink/rest/WaveLinkResource.java similarity index 73% rename from src/main/java/com/getpcpanel/rest/wavelink/WaveLinkResource.java rename to src/main/java/com/getpcpanel/integration/wavelink/rest/WaveLinkResource.java index 1c178f03..0a300cb8 100644 --- a/src/main/java/com/getpcpanel/rest/wavelink/WaveLinkResource.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/rest/WaveLinkResource.java @@ -1,7 +1,7 @@ -package com.getpcpanel.rest.wavelink; +package com.getpcpanel.integration.wavelink.rest; -import com.getpcpanel.rest.wavelink.dto.WaveLinkResponseDto; -import com.getpcpanel.wavelink.WaveLinkService; +import com.getpcpanel.integration.wavelink.rest.dto.WaveLinkResponseDto; +import com.getpcpanel.integration.wavelink.WaveLinkService; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; diff --git a/src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkAppDto.java b/src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkAppDto.java similarity index 84% rename from src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkAppDto.java rename to src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkAppDto.java index 0401e344..ceda94a6 100644 --- a/src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkAppDto.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkAppDto.java @@ -1,4 +1,4 @@ -package com.getpcpanel.rest.wavelink.dto; +package com.getpcpanel.integration.wavelink.rest.dto; import dev.niels.wavelink.impl.model.WaveLinkApp; diff --git a/src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkChannelDto.java b/src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkChannelDto.java similarity index 96% rename from src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkChannelDto.java rename to src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkChannelDto.java index bb5e55c2..f764d22a 100644 --- a/src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkChannelDto.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkChannelDto.java @@ -1,4 +1,4 @@ -package com.getpcpanel.rest.wavelink.dto; +package com.getpcpanel.integration.wavelink.rest.dto; import java.util.List; diff --git a/src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkEffectDto.java b/src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkEffectDto.java similarity index 89% rename from src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkEffectDto.java rename to src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkEffectDto.java index 8e0ed183..1117f9e0 100644 --- a/src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkEffectDto.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkEffectDto.java @@ -1,4 +1,4 @@ -package com.getpcpanel.rest.wavelink.dto; +package com.getpcpanel.integration.wavelink.rest.dto; import dev.niels.wavelink.impl.model.WaveLinkEffect; import jakarta.annotation.Nullable; diff --git a/src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkInputDto.java b/src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkInputDto.java similarity index 87% rename from src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkInputDto.java rename to src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkInputDto.java index afe73c9b..649fa3f8 100644 --- a/src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkInputDto.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkInputDto.java @@ -1,4 +1,4 @@ -package com.getpcpanel.rest.wavelink.dto; +package com.getpcpanel.integration.wavelink.rest.dto; import javax.annotation.Nullable; diff --git a/src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkMixDto.java b/src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkMixDto.java similarity index 86% rename from src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkMixDto.java rename to src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkMixDto.java index d8401b90..fe23b217 100644 --- a/src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkMixDto.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkMixDto.java @@ -1,4 +1,4 @@ -package com.getpcpanel.rest.wavelink.dto; +package com.getpcpanel.integration.wavelink.rest.dto; import dev.niels.wavelink.impl.model.WaveLinkMix; import jakarta.annotation.Nullable; diff --git a/src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkOutputDto.java b/src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkOutputDto.java similarity index 87% rename from src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkOutputDto.java rename to src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkOutputDto.java index f0cff33f..dc342191 100644 --- a/src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkOutputDto.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkOutputDto.java @@ -1,4 +1,4 @@ -package com.getpcpanel.rest.wavelink.dto; +package com.getpcpanel.integration.wavelink.rest.dto; import dev.niels.wavelink.impl.model.WaveLinkOutputDevice; import jakarta.annotation.Nullable; diff --git a/src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkResponseDto.java b/src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkResponseDto.java similarity index 93% rename from src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkResponseDto.java rename to src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkResponseDto.java index d0b4ce12..9f7b4549 100644 --- a/src/main/java/com/getpcpanel/rest/wavelink/dto/WaveLinkResponseDto.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/rest/dto/WaveLinkResponseDto.java @@ -1,8 +1,8 @@ -package com.getpcpanel.rest.wavelink.dto; +package com.getpcpanel.integration.wavelink.rest.dto; import java.util.List; -import com.getpcpanel.wavelink.WaveLinkService; +import com.getpcpanel.integration.wavelink.WaveLinkService; import io.quarkus.runtime.annotations.RegisterForReflection; import one.util.streamex.StreamEx; diff --git a/src/main/java/com/getpcpanel/cpp/linux/LinuxProcessHelper.java b/src/main/java/com/getpcpanel/platform/process/LinuxProcessHelper.java similarity index 99% rename from src/main/java/com/getpcpanel/cpp/linux/LinuxProcessHelper.java rename to src/main/java/com/getpcpanel/platform/process/LinuxProcessHelper.java index 79903b96..ca41d6c7 100644 --- a/src/main/java/com/getpcpanel/cpp/linux/LinuxProcessHelper.java +++ b/src/main/java/com/getpcpanel/platform/process/LinuxProcessHelper.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.linux; +package com.getpcpanel.platform.process; import java.io.File; import java.io.IOException; @@ -20,7 +20,7 @@ import com.getpcpanel.platform.IProcessHelper; import com.getpcpanel.platform.LinuxBuild; -import com.getpcpanel.util.ProcessHelper; +import com.getpcpanel.util.os.ProcessHelper; import io.quarkus.arc.Unremovable; import jakarta.annotation.PostConstruct; diff --git a/src/main/java/com/getpcpanel/cpp/osx/OsxProcessHelper.java b/src/main/java/com/getpcpanel/platform/process/OsxProcessHelper.java similarity index 98% rename from src/main/java/com/getpcpanel/cpp/osx/OsxProcessHelper.java rename to src/main/java/com/getpcpanel/platform/process/OsxProcessHelper.java index 8cb214d1..e7416f85 100644 --- a/src/main/java/com/getpcpanel/cpp/osx/OsxProcessHelper.java +++ b/src/main/java/com/getpcpanel/platform/process/OsxProcessHelper.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.osx; +package com.getpcpanel.platform.process; import java.io.IOException; import java.nio.charset.Charset; @@ -17,7 +17,7 @@ import com.getpcpanel.platform.IProcessHelper; import com.getpcpanel.platform.MacBuild; -import com.getpcpanel.util.ProcessHelper; +import com.getpcpanel.util.os.ProcessHelper; import io.quarkus.arc.Unremovable; import jakarta.enterprise.context.ApplicationScoped; diff --git a/src/main/java/com/getpcpanel/cpp/linux/ProcessConditionalHelper.java b/src/main/java/com/getpcpanel/platform/process/ProcessConditionalHelper.java similarity index 96% rename from src/main/java/com/getpcpanel/cpp/linux/ProcessConditionalHelper.java rename to src/main/java/com/getpcpanel/platform/process/ProcessConditionalHelper.java index b98ac8b9..0dd4decb 100644 --- a/src/main/java/com/getpcpanel/cpp/linux/ProcessConditionalHelper.java +++ b/src/main/java/com/getpcpanel/platform/process/ProcessConditionalHelper.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.linux; +package com.getpcpanel.platform.process; import java.nio.file.Files; import java.nio.file.Path; diff --git a/src/main/java/com/getpcpanel/cpp/windows/WindowsProcessHelper.java b/src/main/java/com/getpcpanel/platform/process/WindowsProcessHelper.java similarity index 96% rename from src/main/java/com/getpcpanel/cpp/windows/WindowsProcessHelper.java rename to src/main/java/com/getpcpanel/platform/process/WindowsProcessHelper.java index 563fe327..00639d77 100644 --- a/src/main/java/com/getpcpanel/cpp/windows/WindowsProcessHelper.java +++ b/src/main/java/com/getpcpanel/platform/process/WindowsProcessHelper.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.windows; +package com.getpcpanel.platform.process; import java.util.OptionalInt; diff --git a/src/main/java/com/getpcpanel/profile/DeviceSave.java b/src/main/java/com/getpcpanel/profile/DeviceSave.java index 6138d108..22cb6f8b 100644 --- a/src/main/java/com/getpcpanel/profile/DeviceSave.java +++ b/src/main/java/com/getpcpanel/profile/DeviceSave.java @@ -9,7 +9,7 @@ import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.getpcpanel.device.DeviceType; +import com.getpcpanel.device.provider.pcpanel.DeviceType; import com.getpcpanel.device.descriptor.DeviceDescriptor; import com.getpcpanel.profile.dto.LightingConfig; diff --git a/src/main/java/com/getpcpanel/profile/LinuxWindowFocusPoller.java b/src/main/java/com/getpcpanel/profile/LinuxWindowFocusPoller.java index aa008550..b7281359 100644 --- a/src/main/java/com/getpcpanel/profile/LinuxWindowFocusPoller.java +++ b/src/main/java/com/getpcpanel/profile/LinuxWindowFocusPoller.java @@ -2,8 +2,7 @@ import org.apache.commons.lang3.StringUtils; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.cpp.windows.WindowFocusChangedEvent; +import com.getpcpanel.integration.volume.platform.ISndCtrl; import com.getpcpanel.platform.LinuxBuild; import io.quarkus.scheduler.Scheduled; diff --git a/src/main/java/com/getpcpanel/profile/Profile.java b/src/main/java/com/getpcpanel/profile/Profile.java index b955a53c..f907c4bc 100644 --- a/src/main/java/com/getpcpanel/profile/Profile.java +++ b/src/main/java/com/getpcpanel/profile/Profile.java @@ -9,10 +9,10 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.getpcpanel.commands.Commands; -import com.getpcpanel.device.DeviceType; +import com.getpcpanel.device.provider.pcpanel.DeviceType; import com.getpcpanel.profile.dto.KnobSetting; import com.getpcpanel.profile.dto.LightingConfig; -import com.getpcpanel.profile.dto.OSCBinding; +import com.getpcpanel.integration.osc.dto.OSCBinding; import lombok.Data; diff --git a/src/main/java/com/getpcpanel/profile/ProfileWindowFocusService.java b/src/main/java/com/getpcpanel/profile/ProfileWindowFocusService.java index fc95107c..2299aca3 100644 --- a/src/main/java/com/getpcpanel/profile/ProfileWindowFocusService.java +++ b/src/main/java/com/getpcpanel/profile/ProfileWindowFocusService.java @@ -4,8 +4,7 @@ import jakarta.enterprise.event.Observes; import jakarta.enterprise.context.ApplicationScoped; -import com.getpcpanel.cpp.windows.WindowFocusChangedEvent; -import com.getpcpanel.hid.DeviceHolder; +import com.getpcpanel.device.DeviceHolder; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/getpcpanel/profile/Save.java b/src/main/java/com/getpcpanel/profile/Save.java index 26f96ece..ab86791a 100644 --- a/src/main/java/com/getpcpanel/profile/Save.java +++ b/src/main/java/com/getpcpanel/profile/Save.java @@ -8,17 +8,17 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -import com.getpcpanel.device.DeviceType; +import com.getpcpanel.device.provider.pcpanel.DeviceType; import com.getpcpanel.device.descriptor.DeviceDescriptor; -import com.getpcpanel.homeassistant.dto.HomeAssistantServer; -import com.getpcpanel.profile.dto.DiscordAuth; -import com.getpcpanel.profile.dto.DiscordSeenUser; -import com.getpcpanel.profile.dto.DiscordSettings; -import com.getpcpanel.profile.dto.FocusVolumeOverride; -import com.getpcpanel.profile.dto.MqttSettings; -import com.getpcpanel.profile.dto.OSCConnectionInfo; -import com.getpcpanel.profile.dto.OverlayPosition; -import com.getpcpanel.profile.dto.WaveLinkSettings; +import com.getpcpanel.integration.homeassistant.dto.HomeAssistantServer; +import com.getpcpanel.integration.discord.dto.DiscordAuth; +import com.getpcpanel.integration.discord.dto.DiscordSeenUser; +import com.getpcpanel.integration.discord.dto.DiscordSettings; +import com.getpcpanel.integration.volume.FocusVolumeOverride; +import com.getpcpanel.integration.mqtt.dto.MqttSettings; +import com.getpcpanel.integration.osc.dto.OSCConnectionInfo; +import com.getpcpanel.integration.volume.overlay.OverlayPosition; +import com.getpcpanel.integration.wavelink.dto.WaveLinkSettings; import lombok.Data; import lombok.extern.log4j.Log4j2; diff --git a/src/main/java/com/getpcpanel/profile/SaveService.java b/src/main/java/com/getpcpanel/profile/SaveService.java index 19d8cc6f..7adf2bb8 100644 --- a/src/main/java/com/getpcpanel/profile/SaveService.java +++ b/src/main/java/com/getpcpanel/profile/SaveService.java @@ -11,11 +11,11 @@ import org.apache.commons.lang3.StringUtils; import com.getpcpanel.Json; -import com.getpcpanel.device.DescriptorFactory; +import com.getpcpanel.device.provider.pcpanel.DescriptorFactory; import com.getpcpanel.device.Device; -import com.getpcpanel.hid.DeviceHolder; -import com.getpcpanel.util.Debouncer; -import com.getpcpanel.util.FileUtil; +import com.getpcpanel.device.DeviceHolder; +import com.getpcpanel.util.concurrent.Debouncer; +import com.getpcpanel.util.io.FileUtil; import com.getpcpanel.util.tray.win.WinUser32Ext; import com.sun.jna.WString; diff --git a/src/main/java/com/getpcpanel/util/ShortcutHook.java b/src/main/java/com/getpcpanel/profile/ShortcutHook.java similarity index 96% rename from src/main/java/com/getpcpanel/util/ShortcutHook.java rename to src/main/java/com/getpcpanel/profile/ShortcutHook.java index 5be86192..bf4b07a0 100644 --- a/src/main/java/com/getpcpanel/util/ShortcutHook.java +++ b/src/main/java/com/getpcpanel/profile/ShortcutHook.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.profile; import java.util.Map; import java.util.Set; @@ -8,9 +8,7 @@ import org.apache.commons.lang3.StringUtils; import jakarta.enterprise.context.ApplicationScoped; -import com.getpcpanel.hid.DeviceHolder; -import com.getpcpanel.profile.Profile; -import com.getpcpanel.profile.SaveService; +import com.getpcpanel.device.DeviceHolder; import com.getpcpanel.platform.WindowsBuild; import com.github.kwhat.jnativehook.GlobalScreen; import com.github.kwhat.jnativehook.NativeHookException; diff --git a/src/main/java/com/getpcpanel/cpp/windows/WindowFocusChangedEvent.java b/src/main/java/com/getpcpanel/profile/WindowFocusChangedEvent.java similarity index 63% rename from src/main/java/com/getpcpanel/cpp/windows/WindowFocusChangedEvent.java rename to src/main/java/com/getpcpanel/profile/WindowFocusChangedEvent.java index 9fb66ed6..3743a2d5 100644 --- a/src/main/java/com/getpcpanel/cpp/windows/WindowFocusChangedEvent.java +++ b/src/main/java/com/getpcpanel/profile/WindowFocusChangedEvent.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.windows; +package com.getpcpanel.profile; public record WindowFocusChangedEvent(String application) { } diff --git a/src/main/java/com/getpcpanel/profile/dto/LightingConfig.java b/src/main/java/com/getpcpanel/profile/dto/LightingConfig.java index 45f457ec..486f6406 100644 --- a/src/main/java/com/getpcpanel/profile/dto/LightingConfig.java +++ b/src/main/java/com/getpcpanel/profile/dto/LightingConfig.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; -import com.getpcpanel.device.DeviceType; +import com.getpcpanel.device.provider.pcpanel.DeviceType; import com.getpcpanel.device.descriptor.DeviceDescriptor; import lombok.AllArgsConstructor; diff --git a/src/main/java/com/getpcpanel/rest/CommandsResource.java b/src/main/java/com/getpcpanel/rest/CommandsResource.java deleted file mode 100644 index e35e1cdc..00000000 --- a/src/main/java/com/getpcpanel/rest/CommandsResource.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.getpcpanel.rest; - -import java.util.Collection; -import java.util.List; - -import com.getpcpanel.commands.command.CommandBrightness; -import com.getpcpanel.commands.command.CommandEndProgram; -import com.getpcpanel.commands.command.CommandKeystroke; -import com.getpcpanel.commands.command.CommandMedia; -import com.getpcpanel.commands.command.CommandObsMuteSource; -import com.getpcpanel.commands.command.CommandObsSetScene; -import com.getpcpanel.commands.command.CommandObsSetSourceVolume; -import com.getpcpanel.commands.command.CommandRun; -import com.getpcpanel.commands.command.CommandShortcut; -import com.getpcpanel.commands.command.CommandVoiceMeeterAdvanced; -import com.getpcpanel.commands.command.CommandVoiceMeeterAdvancedButton; -import com.getpcpanel.commands.command.CommandVoiceMeeterBasic; -import com.getpcpanel.commands.command.CommandVoiceMeeterBasicButton; -import com.getpcpanel.commands.command.CommandVolumeApplicationDeviceToggle; -import com.getpcpanel.commands.command.CommandVolumeDefaultDevice; -import com.getpcpanel.commands.command.CommandVolumeDefaultDeviceAdvanced; -import com.getpcpanel.commands.command.CommandVolumeDefaultDeviceToggle; -import com.getpcpanel.commands.command.CommandVolumeDefaultDeviceToggleAdvanced; -import com.getpcpanel.commands.command.CommandVolumeDevice; -import com.getpcpanel.commands.command.CommandVolumeDeviceMute; -import com.getpcpanel.commands.command.CommandVolumeFocus; -import com.getpcpanel.commands.command.CommandVolumeFocusMute; -import com.getpcpanel.commands.command.CommandVolumeProcess; -import com.getpcpanel.commands.command.CommandVolumeProcessMute; -import com.getpcpanel.profile.SaveService; -import com.getpcpanel.rest.EventBroadcaster.AssignmentChangedEvent.Kinds; -import com.getpcpanel.rest.model.dto.CommandType; -import com.getpcpanel.rest.model.dto.CommandType.CommandCategory; -import com.getpcpanel.wavelink.command.CommandWaveLinkAddFocusToChannel; -import com.getpcpanel.wavelink.command.CommandWaveLinkChangeLevel; -import com.getpcpanel.wavelink.command.CommandWaveLinkChangeMute; -import com.getpcpanel.wavelink.command.CommandWaveLinkChannelEffect; -import com.getpcpanel.wavelink.command.CommandWaveLinkMainOutput; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import one.util.streamex.StreamEx; - -@Path("/api/commands") -@ApplicationScoped -public class CommandsResource { - @Inject SaveService saveService; - - private static final List commandTypes = List.of( - new CommandType("Brightness", CommandBrightness.class.getName(), CommandCategory.standard, Kinds.dial), - new CommandType("Process volume", CommandVolumeProcess.class.getName(), CommandCategory.standard, Kinds.dial), - new CommandType("Focus volume", CommandVolumeFocus.class.getName(), CommandCategory.standard, Kinds.dial), - new CommandType("Device volume", CommandVolumeDevice.class.getName(), CommandCategory.standard, Kinds.dial), - new CommandType("Obs Source Volume", CommandObsSetSourceVolume.class.getName(), CommandCategory.obs, Kinds.dial), - new CommandType("VoiceMeeter Advanced", CommandVoiceMeeterAdvanced.class.getName(), CommandCategory.voicemeeter, Kinds.dial), - new CommandType("VoiceMeeter Basic", CommandVoiceMeeterBasic.class.getName(), CommandCategory.voicemeeter, Kinds.dial), - new CommandType("WaveLink Change Level", CommandWaveLinkChangeLevel.class.getName(), CommandCategory.wavelink, Kinds.dial), - - new CommandType("End Program", CommandEndProgram.class.getName(), CommandCategory.standard, Kinds.button), - new CommandType("Keystroke", CommandKeystroke.class.getName(), CommandCategory.standard, Kinds.button), - new CommandType("Run", CommandRun.class.getName(), CommandCategory.standard, Kinds.button), - new CommandType("Shortcut", CommandShortcut.class.getName(), CommandCategory.standard, Kinds.button), - new CommandType("Media", CommandMedia.class.getName(), CommandCategory.standard, Kinds.button), - new CommandType("Toggle application device", CommandVolumeApplicationDeviceToggle.class.getName(), CommandCategory.standard, Kinds.button), - new CommandType("Default Device", CommandVolumeDefaultDevice.class.getName(), CommandCategory.standard, Kinds.button), - new CommandType("Default Device Advanced", CommandVolumeDefaultDeviceAdvanced.class.getName(), CommandCategory.standard, Kinds.button), - new CommandType("Default Device Toggle", CommandVolumeDefaultDeviceToggle.class.getName(), CommandCategory.standard, Kinds.button), - new CommandType("Default Device Toggle Advanced", CommandVolumeDefaultDeviceToggleAdvanced.class.getName(), CommandCategory.standard, Kinds.button), - new CommandType("Device Mute", CommandVolumeDeviceMute.class.getName(), CommandCategory.standard, Kinds.button), - new CommandType("Focus Mute", CommandVolumeFocusMute.class.getName(), CommandCategory.standard, Kinds.button), - new CommandType("Process Mute", CommandVolumeProcessMute.class.getName(), CommandCategory.standard, Kinds.button), - new CommandType("Obs Mute Source", CommandObsMuteSource.class.getName(), CommandCategory.obs, Kinds.button), - new CommandType("Obs Set Scene", CommandObsSetScene.class.getName(), CommandCategory.obs, Kinds.button), - new CommandType("VoiceMeeter Advanced", CommandVoiceMeeterAdvancedButton.class.getName(), CommandCategory.voicemeeter, Kinds.button), - new CommandType("VoiceMeeter Basic", CommandVoiceMeeterBasicButton.class.getName(), CommandCategory.voicemeeter, Kinds.button), - new CommandType("WaveLink Add Focus To Channel", CommandWaveLinkAddFocusToChannel.class.getName(), CommandCategory.wavelink, Kinds.button), - new CommandType("WaveLink Change Mute", CommandWaveLinkChangeMute.class.getName(), CommandCategory.wavelink, Kinds.button), - new CommandType("WaveLink Channel Effect", CommandWaveLinkChannelEffect.class.getName(), CommandCategory.wavelink, Kinds.button), - new CommandType("WaveLink Main Output", CommandWaveLinkMainOutput.class.getName(), CommandCategory.wavelink, Kinds.button) - ); - - @GET - @Path("/available") - public Collection listAvailableCommands() { - return StreamEx.of(commandTypes).filter(this::enabled).toList(); - } - - private boolean enabled(CommandType commandType) { - return switch (commandType.category()) { - case standard -> true; - case obs -> saveService.get().isObsEnabled(); - case voicemeeter -> saveService.get().isVoicemeeterEnabled(); - case wavelink -> saveService.get().getWaveLink().enabled(); - }; - } -} diff --git a/src/main/java/com/getpcpanel/rest/EventBroadcaster.java b/src/main/java/com/getpcpanel/rest/EventBroadcaster.java index 9f37aac0..b2e04e2b 100644 --- a/src/main/java/com/getpcpanel/rest/EventBroadcaster.java +++ b/src/main/java/com/getpcpanel/rest/EventBroadcaster.java @@ -1,18 +1,19 @@ package com.getpcpanel.rest; +import com.getpcpanel.device.provider.pcpanel.ProVisualColorsService; import com.fasterxml.jackson.databind.ObjectMapper; import com.getpcpanel.commands.Commands; -import com.getpcpanel.hid.DeviceCommunicationHandler.ButtonPressEvent; -import com.getpcpanel.hid.DeviceCommunicationHandler.KnobRotateEvent; -import com.getpcpanel.hid.DeviceHolder; -import com.getpcpanel.hid.DeviceHolder.DeviceFullyConnectedEvent; -import com.getpcpanel.hid.DeviceScanner.DeviceDisconnectedEvent; +import com.getpcpanel.device.provider.pcpanel.DeviceCommunicationHandler.ButtonPressEvent; +import com.getpcpanel.device.provider.pcpanel.DeviceCommunicationHandler.KnobRotateEvent; +import com.getpcpanel.device.DeviceHolder; +import com.getpcpanel.device.DeviceHolder.DeviceFullyConnectedEvent; +import com.getpcpanel.device.provider.pcpanel.DeviceScanner.DeviceDisconnectedEvent; import com.getpcpanel.profile.Profile; import com.getpcpanel.profile.ProfileSwitchedEvent; import com.getpcpanel.profile.SaveService; import com.getpcpanel.profile.dto.KnobSetting; import com.getpcpanel.profile.dto.LightingConfig; -import com.getpcpanel.rest.ProVisualColorsService.ProVisualColors; +import com.getpcpanel.device.provider.pcpanel.ProVisualColorsService.ProVisualColors; import com.getpcpanel.rest.model.dto.DeviceSnapshotDto; import com.getpcpanel.rest.model.dto.ProfileSnapshotDto; import com.getpcpanel.rest.model.ws.WsAssignmentChangedEvent; @@ -26,7 +27,7 @@ import com.getpcpanel.rest.model.ws.WsNewVersionAvailableEvent; import com.getpcpanel.rest.model.ws.WsProfileSwitchedEvent; import com.getpcpanel.rest.model.ws.WsVisualColorsChangedEvent; -import com.getpcpanel.util.AppShutdownState; +import com.getpcpanel.util.app.AppShutdownState; import com.getpcpanel.util.version.VersionChecker.NewVersionAvailableEvent; import jakarta.enterprise.context.ApplicationScoped; diff --git a/src/main/java/com/getpcpanel/rest/EventWebSocket.java b/src/main/java/com/getpcpanel/rest/EventWebSocket.java index c64cb4b8..a2dae87b 100644 --- a/src/main/java/com/getpcpanel/rest/EventWebSocket.java +++ b/src/main/java/com/getpcpanel/rest/EventWebSocket.java @@ -1,14 +1,15 @@ package com.getpcpanel.rest; +import com.getpcpanel.device.provider.pcpanel.ProVisualColorsService; import java.util.concurrent.CopyOnWriteArraySet; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.getpcpanel.hid.DeviceHolder; +import com.getpcpanel.device.DeviceHolder; import com.getpcpanel.profile.SaveService; import com.getpcpanel.rest.model.dto.DeviceSnapshotDto; import com.getpcpanel.rest.model.ws.WsDeviceConnectedEvent; -import com.getpcpanel.util.AppShutdownState; +import com.getpcpanel.util.app.AppShutdownState; import io.quarkus.websockets.next.OnClose; import io.quarkus.websockets.next.OnOpen; diff --git a/src/main/java/com/getpcpanel/rest/IconResource.java b/src/main/java/com/getpcpanel/rest/IconResource.java index 434b95ac..6aff4a53 100644 --- a/src/main/java/com/getpcpanel/rest/IconResource.java +++ b/src/main/java/com/getpcpanel/rest/IconResource.java @@ -14,7 +14,7 @@ import jakarta.ws.rs.core.Response; import com.getpcpanel.iconextract.IIconService; -import com.getpcpanel.util.PngEncoder; +import com.getpcpanel.util.image.PngEncoder; import lombok.extern.log4j.Log4j2; diff --git a/src/main/java/com/getpcpanel/rest/ProcessResource.java b/src/main/java/com/getpcpanel/rest/ProcessResource.java index 58ddb1f5..29d4d85d 100644 --- a/src/main/java/com/getpcpanel/rest/ProcessResource.java +++ b/src/main/java/com/getpcpanel/rest/ProcessResource.java @@ -7,10 +7,10 @@ import javax.annotation.Nullable; -import com.getpcpanel.cpp.ISndCtrl; +import com.getpcpanel.integration.volume.platform.ISndCtrl; import com.getpcpanel.iconextract.IIconService; import com.getpcpanel.rest.model.dto.ProcessDto; -import com.getpcpanel.util.PngEncoder; +import com.getpcpanel.util.image.PngEncoder; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; diff --git a/src/main/java/com/getpcpanel/rest/SettingsResource.java b/src/main/java/com/getpcpanel/rest/SettingsResource.java index e7cc37a7..74418074 100644 --- a/src/main/java/com/getpcpanel/rest/SettingsResource.java +++ b/src/main/java/com/getpcpanel/rest/SettingsResource.java @@ -2,11 +2,11 @@ import java.util.Map; -import com.getpcpanel.mqtt.MqttService; +import com.getpcpanel.integration.mqtt.MqttService; import com.getpcpanel.profile.SaveService; -import com.getpcpanel.profile.dto.DiscordSettings; -import com.getpcpanel.profile.dto.MqttSettings; -import com.getpcpanel.profile.dto.WaveLinkSettings; +import com.getpcpanel.integration.discord.dto.DiscordSettings; +import com.getpcpanel.integration.mqtt.dto.MqttSettings; +import com.getpcpanel.integration.wavelink.dto.WaveLinkSettings; import com.getpcpanel.rest.model.dto.SettingsDto; import jakarta.enterprise.context.ApplicationScoped; diff --git a/src/main/java/com/getpcpanel/rest/SystemResource.java b/src/main/java/com/getpcpanel/rest/SystemResource.java index 8db3eacb..523af207 100644 --- a/src/main/java/com/getpcpanel/rest/SystemResource.java +++ b/src/main/java/com/getpcpanel/rest/SystemResource.java @@ -10,7 +10,7 @@ import jakarta.ws.rs.core.Response; import com.getpcpanel.rest.model.dto.OnboardingDto; -import com.getpcpanel.util.StartupOnboarding; +import com.getpcpanel.util.app.StartupOnboarding; import io.quarkus.runtime.Quarkus; import lombok.extern.log4j.Log4j2; diff --git a/src/main/java/com/getpcpanel/rest/model/dto/CommandType.java b/src/main/java/com/getpcpanel/rest/model/dto/CommandType.java deleted file mode 100644 index 4b0217d6..00000000 --- a/src/main/java/com/getpcpanel/rest/model/dto/CommandType.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.getpcpanel.rest.model.dto; - -import com.getpcpanel.rest.EventBroadcaster.AssignmentChangedEvent.Kinds; - -public record CommandType( - String name, - String command, - CommandCategory category, - Kinds kind -) { - - public enum CommandCategory { - standard, voicemeeter, obs, wavelink - } -} diff --git a/src/main/java/com/getpcpanel/rest/model/dto/DeviceDto.java b/src/main/java/com/getpcpanel/rest/model/dto/DeviceDto.java index a44854e3..476394e3 100644 --- a/src/main/java/com/getpcpanel/rest/model/dto/DeviceDto.java +++ b/src/main/java/com/getpcpanel/rest/model/dto/DeviceDto.java @@ -5,7 +5,7 @@ import javax.annotation.Nullable; import com.getpcpanel.device.Device; -import com.getpcpanel.device.DeviceType; +import com.getpcpanel.device.provider.pcpanel.DeviceType; import com.getpcpanel.device.descriptor.DeviceDescriptor; import com.getpcpanel.device.descriptor.LightGroupKind; import com.getpcpanel.profile.DeviceSave; diff --git a/src/main/java/com/getpcpanel/rest/model/dto/DeviceSnapshotDto.java b/src/main/java/com/getpcpanel/rest/model/dto/DeviceSnapshotDto.java index 7048749b..e209a7b8 100644 --- a/src/main/java/com/getpcpanel/rest/model/dto/DeviceSnapshotDto.java +++ b/src/main/java/com/getpcpanel/rest/model/dto/DeviceSnapshotDto.java @@ -12,7 +12,7 @@ import com.getpcpanel.profile.DeviceSave; import com.getpcpanel.profile.Profile; import com.getpcpanel.profile.dto.LightingConfig; -import com.getpcpanel.rest.ProVisualColorsService; +import com.getpcpanel.device.provider.pcpanel.ProVisualColorsService; import com.getpcpanel.rest.model.ws.WsEvent; import one.util.streamex.StreamEx; diff --git a/src/main/java/com/getpcpanel/rest/model/dto/SettingsDto.java b/src/main/java/com/getpcpanel/rest/model/dto/SettingsDto.java index 2b17394f..829e69ee 100644 --- a/src/main/java/com/getpcpanel/rest/model/dto/SettingsDto.java +++ b/src/main/java/com/getpcpanel/rest/model/dto/SettingsDto.java @@ -4,12 +4,12 @@ import javax.annotation.Nullable; -import com.getpcpanel.homeassistant.dto.HomeAssistantServer; +import com.getpcpanel.integration.homeassistant.dto.HomeAssistantServer; import com.getpcpanel.profile.Save; -import com.getpcpanel.profile.dto.FocusVolumeOverride; -import com.getpcpanel.profile.dto.MqttSettings; -import com.getpcpanel.profile.dto.OSCConnectionInfo; -import com.getpcpanel.profile.dto.OverlayPosition; +import com.getpcpanel.integration.volume.FocusVolumeOverride; +import com.getpcpanel.integration.mqtt.dto.MqttSettings; +import com.getpcpanel.integration.osc.dto.OSCConnectionInfo; +import com.getpcpanel.integration.volume.overlay.OverlayPosition; import lombok.Data; import lombok.NoArgsConstructor; diff --git a/src/main/java/com/getpcpanel/sleepdetection/SleepDetector.java b/src/main/java/com/getpcpanel/sleepdetection/SleepDetector.java index b4fae301..8e0f3aae 100644 --- a/src/main/java/com/getpcpanel/sleepdetection/SleepDetector.java +++ b/src/main/java/com/getpcpanel/sleepdetection/SleepDetector.java @@ -1,9 +1,9 @@ package com.getpcpanel.sleepdetection; import com.getpcpanel.device.Device; -import com.getpcpanel.hid.DeviceHolder; -import com.getpcpanel.hid.DeviceScanner; -import com.getpcpanel.hid.OutputInterpreter; +import com.getpcpanel.device.DeviceHolder; +import com.getpcpanel.device.provider.pcpanel.DeviceScanner; +import com.getpcpanel.device.provider.pcpanel.OutputInterpreter; import com.getpcpanel.profile.dto.LightingConfig; import com.getpcpanel.sleepdetection.DarkReasonGate.Reason; diff --git a/src/main/java/com/getpcpanel/util/AppEvents.java b/src/main/java/com/getpcpanel/util/app/AppEvents.java similarity index 95% rename from src/main/java/com/getpcpanel/util/AppEvents.java rename to src/main/java/com/getpcpanel/util/app/AppEvents.java index bb5c5512..1d2ea49f 100644 --- a/src/main/java/com/getpcpanel/util/AppEvents.java +++ b/src/main/java/com/getpcpanel/util/app/AppEvents.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.app; import io.quarkus.runtime.Startup; import jakarta.annotation.PostConstruct; diff --git a/src/main/java/com/getpcpanel/util/AppShutdownState.java b/src/main/java/com/getpcpanel/util/app/AppShutdownState.java similarity index 96% rename from src/main/java/com/getpcpanel/util/AppShutdownState.java rename to src/main/java/com/getpcpanel/util/app/AppShutdownState.java index ed17fa5e..cc2e1d99 100644 --- a/src/main/java/com/getpcpanel/util/AppShutdownState.java +++ b/src/main/java/com/getpcpanel/util/app/AppShutdownState.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.app; import java.util.concurrent.atomic.AtomicBoolean; diff --git a/src/main/java/com/getpcpanel/util/OpenFolderEvent.java b/src/main/java/com/getpcpanel/util/app/OpenFolderEvent.java similarity index 81% rename from src/main/java/com/getpcpanel/util/OpenFolderEvent.java rename to src/main/java/com/getpcpanel/util/app/OpenFolderEvent.java index 4a8389f6..70155f7d 100644 --- a/src/main/java/com/getpcpanel/util/OpenFolderEvent.java +++ b/src/main/java/com/getpcpanel/util/app/OpenFolderEvent.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.app; /** Request to reveal a folder in the OS file manager (handled by {@link ShowMainService}). */ public record OpenFolderEvent(String path) { diff --git a/src/main/java/com/getpcpanel/util/ShowMainEvent.java b/src/main/java/com/getpcpanel/util/app/ShowMainEvent.java similarity index 80% rename from src/main/java/com/getpcpanel/util/ShowMainEvent.java rename to src/main/java/com/getpcpanel/util/app/ShowMainEvent.java index 7db5801c..78c882a6 100644 --- a/src/main/java/com/getpcpanel/util/ShowMainEvent.java +++ b/src/main/java/com/getpcpanel/util/app/ShowMainEvent.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.app; /** * Event fired when another instance of the application requests the main window to be shown. diff --git a/src/main/java/com/getpcpanel/util/ShowMainService.java b/src/main/java/com/getpcpanel/util/app/ShowMainService.java similarity index 98% rename from src/main/java/com/getpcpanel/util/ShowMainService.java rename to src/main/java/com/getpcpanel/util/app/ShowMainService.java index e70f328d..95c436ce 100644 --- a/src/main/java/com/getpcpanel/util/ShowMainService.java +++ b/src/main/java/com/getpcpanel/util/app/ShowMainService.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.app; import java.io.IOException; import java.nio.file.Files; diff --git a/src/main/java/com/getpcpanel/util/StartupOnboarding.java b/src/main/java/com/getpcpanel/util/app/StartupOnboarding.java similarity index 99% rename from src/main/java/com/getpcpanel/util/StartupOnboarding.java rename to src/main/java/com/getpcpanel/util/app/StartupOnboarding.java index 95f1e611..d321a4b5 100644 --- a/src/main/java/com/getpcpanel/util/StartupOnboarding.java +++ b/src/main/java/com/getpcpanel/util/app/StartupOnboarding.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.app; import org.apache.commons.lang3.StringUtils; import org.eclipse.microprofile.config.inject.ConfigProperty; diff --git a/src/main/java/com/getpcpanel/util/Debouncer.java b/src/main/java/com/getpcpanel/util/concurrent/Debouncer.java similarity index 99% rename from src/main/java/com/getpcpanel/util/Debouncer.java rename to src/main/java/com/getpcpanel/util/concurrent/Debouncer.java index 3a16eeaa..57890902 100644 --- a/src/main/java/com/getpcpanel/util/Debouncer.java +++ b/src/main/java/com/getpcpanel/util/concurrent/Debouncer.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.concurrent; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; diff --git a/src/main/java/com/getpcpanel/util/ReconnectBackoff.java b/src/main/java/com/getpcpanel/util/concurrent/ReconnectBackoff.java similarity index 98% rename from src/main/java/com/getpcpanel/util/ReconnectBackoff.java rename to src/main/java/com/getpcpanel/util/concurrent/ReconnectBackoff.java index 7f67362e..f452c811 100644 --- a/src/main/java/com/getpcpanel/util/ReconnectBackoff.java +++ b/src/main/java/com/getpcpanel/util/concurrent/ReconnectBackoff.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.concurrent; /** * Exponential-backoff gate for the integration reconnect loops (OBS, Wave Link, …). Each integration diff --git a/src/main/java/com/getpcpanel/util/Images.java b/src/main/java/com/getpcpanel/util/image/Images.java similarity index 86% rename from src/main/java/com/getpcpanel/util/Images.java rename to src/main/java/com/getpcpanel/util/image/Images.java index b9bdcf84..a53ec664 100644 --- a/src/main/java/com/getpcpanel/util/Images.java +++ b/src/main/java/com/getpcpanel/util/image/Images.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.image; /** * Image resources stub — JavaFX removed as part of Quarkus migration. diff --git a/src/main/java/com/getpcpanel/util/PngDecoder.java b/src/main/java/com/getpcpanel/util/image/PngDecoder.java similarity index 99% rename from src/main/java/com/getpcpanel/util/PngDecoder.java rename to src/main/java/com/getpcpanel/util/image/PngDecoder.java index c28dece5..25a9ae7c 100644 --- a/src/main/java/com/getpcpanel/util/PngDecoder.java +++ b/src/main/java/com/getpcpanel/util/image/PngDecoder.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.image; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; diff --git a/src/main/java/com/getpcpanel/util/PngEncoder.java b/src/main/java/com/getpcpanel/util/image/PngEncoder.java similarity index 99% rename from src/main/java/com/getpcpanel/util/PngEncoder.java rename to src/main/java/com/getpcpanel/util/image/PngEncoder.java index 60cf9219..5bafb954 100644 --- a/src/main/java/com/getpcpanel/util/PngEncoder.java +++ b/src/main/java/com/getpcpanel/util/image/PngEncoder.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.image; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; diff --git a/src/main/java/com/getpcpanel/util/ExtractUtil.java b/src/main/java/com/getpcpanel/util/io/ExtractUtil.java similarity index 95% rename from src/main/java/com/getpcpanel/util/ExtractUtil.java rename to src/main/java/com/getpcpanel/util/io/ExtractUtil.java index ad1e89e1..5d072583 100644 --- a/src/main/java/com/getpcpanel/util/ExtractUtil.java +++ b/src/main/java/com/getpcpanel/util/io/ExtractUtil.java @@ -1,4 +1,6 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.io; + +import com.getpcpanel.util.Util; import java.io.File; import java.io.IOException; diff --git a/src/main/java/com/getpcpanel/util/FileChecker.java b/src/main/java/com/getpcpanel/util/io/FileChecker.java similarity index 97% rename from src/main/java/com/getpcpanel/util/FileChecker.java rename to src/main/java/com/getpcpanel/util/io/FileChecker.java index 3dad4fcb..8722919d 100644 --- a/src/main/java/com/getpcpanel/util/FileChecker.java +++ b/src/main/java/com/getpcpanel/util/io/FileChecker.java @@ -1,4 +1,7 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.io; + +import com.getpcpanel.util.app.ShowMainEvent; +import com.getpcpanel.util.app.AppEvents; import java.io.File; import java.io.IOException; diff --git a/src/main/java/com/getpcpanel/util/FileUtil.java b/src/main/java/com/getpcpanel/util/io/FileUtil.java similarity index 95% rename from src/main/java/com/getpcpanel/util/FileUtil.java rename to src/main/java/com/getpcpanel/util/io/FileUtil.java index 13da3c4d..8c6091b4 100644 --- a/src/main/java/com/getpcpanel/util/FileUtil.java +++ b/src/main/java/com/getpcpanel/util/io/FileUtil.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.io; import java.io.File; diff --git a/src/main/java/com/getpcpanel/util/PcPanelRoot.java b/src/main/java/com/getpcpanel/util/io/PcPanelRoot.java similarity index 96% rename from src/main/java/com/getpcpanel/util/PcPanelRoot.java rename to src/main/java/com/getpcpanel/util/io/PcPanelRoot.java index 4e97b9ff..5901250e 100644 --- a/src/main/java/com/getpcpanel/util/PcPanelRoot.java +++ b/src/main/java/com/getpcpanel/util/io/PcPanelRoot.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.io; import java.nio.file.Files; import java.nio.file.Path; @@ -12,7 +12,7 @@ * static field or a build-time constant: in a GraalVM native image a static initializer runs at * build time and would bake in the build machine's home/environment (e.g. the CI runner's * {@code C:\Users\runneradmin}). {@link com.getpcpanel.Main}, {@link FileChecker} and - * {@link com.getpcpanel.hid.HidDebug} each resolve the root through here so the three agree. + * {@link com.getpcpanel.device.provider.pcpanel.HidDebug} each resolve the root through here so the three agree. * *

On Linux the directory follows the XDG Base Directory spec so config lands in a * sandbox-friendly, persisted location. This matters for two shipped targets: diff --git a/src/main/java/com/getpcpanel/util/ConsoleSupport.java b/src/main/java/com/getpcpanel/util/os/ConsoleSupport.java similarity index 97% rename from src/main/java/com/getpcpanel/util/ConsoleSupport.java rename to src/main/java/com/getpcpanel/util/os/ConsoleSupport.java index 67430206..8a21a4f2 100644 --- a/src/main/java/com/getpcpanel/util/ConsoleSupport.java +++ b/src/main/java/com/getpcpanel/util/os/ConsoleSupport.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.os; import java.io.FileOutputStream; import java.io.PrintStream; diff --git a/src/main/java/com/getpcpanel/util/Kernel32Console.java b/src/main/java/com/getpcpanel/util/os/Kernel32Console.java similarity index 93% rename from src/main/java/com/getpcpanel/util/Kernel32Console.java rename to src/main/java/com/getpcpanel/util/os/Kernel32Console.java index 0064d529..c10fdfc5 100644 --- a/src/main/java/com/getpcpanel/util/Kernel32Console.java +++ b/src/main/java/com/getpcpanel/util/os/Kernel32Console.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.os; import com.sun.jna.Native; import com.sun.jna.win32.StdCallLibrary; diff --git a/src/main/java/com/getpcpanel/util/OsxPermissionHelper.java b/src/main/java/com/getpcpanel/util/os/OsxPermissionHelper.java similarity index 98% rename from src/main/java/com/getpcpanel/util/OsxPermissionHelper.java rename to src/main/java/com/getpcpanel/util/os/OsxPermissionHelper.java index ec921f72..96ef1727 100644 --- a/src/main/java/com/getpcpanel/util/OsxPermissionHelper.java +++ b/src/main/java/com/getpcpanel/util/os/OsxPermissionHelper.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.os; import org.apache.commons.lang3.SystemUtils; diff --git a/src/main/java/com/getpcpanel/util/ProcessHelper.java b/src/main/java/com/getpcpanel/util/os/ProcessHelper.java similarity index 90% rename from src/main/java/com/getpcpanel/util/ProcessHelper.java rename to src/main/java/com/getpcpanel/util/os/ProcessHelper.java index 4e7d86f0..4469c8f7 100644 --- a/src/main/java/com/getpcpanel/util/ProcessHelper.java +++ b/src/main/java/com/getpcpanel/util/os/ProcessHelper.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.os; import jakarta.enterprise.context.ApplicationScoped; diff --git a/src/main/java/com/getpcpanel/util/tray/wayland/DBusMenuImpl.java b/src/main/java/com/getpcpanel/util/tray/wayland/DBusMenuImpl.java index 779ab512..80188d44 100644 --- a/src/main/java/com/getpcpanel/util/tray/wayland/DBusMenuImpl.java +++ b/src/main/java/com/getpcpanel/util/tray/wayland/DBusMenuImpl.java @@ -8,11 +8,11 @@ import org.freedesktop.dbus.types.UInt32; import org.freedesktop.dbus.types.Variant; -import com.getpcpanel.util.AppEvents; +import com.getpcpanel.util.app.AppEvents; import com.getpcpanel.util.CdiHelper; -import com.getpcpanel.util.FileUtil; -import com.getpcpanel.util.OpenFolderEvent; -import com.getpcpanel.util.ShowMainEvent; +import com.getpcpanel.util.io.FileUtil; +import com.getpcpanel.util.app.OpenFolderEvent; +import com.getpcpanel.util.app.ShowMainEvent; import io.quarkus.runtime.Quarkus; import io.quarkus.runtime.annotations.RegisterForReflection; diff --git a/src/main/java/com/getpcpanel/util/tray/wayland/StatusNotifierItemImpl.java b/src/main/java/com/getpcpanel/util/tray/wayland/StatusNotifierItemImpl.java index c529710b..3dc54c53 100644 --- a/src/main/java/com/getpcpanel/util/tray/wayland/StatusNotifierItemImpl.java +++ b/src/main/java/com/getpcpanel/util/tray/wayland/StatusNotifierItemImpl.java @@ -2,8 +2,8 @@ import static com.getpcpanel.util.tray.wayland.TrayServiceWayland.SNI_BUS_NAME; -import com.getpcpanel.util.AppEvents; -import com.getpcpanel.util.ShowMainEvent; +import com.getpcpanel.util.app.AppEvents; +import com.getpcpanel.util.app.ShowMainEvent; import org.freedesktop.dbus.annotations.DBusInterfaceName; import lombok.extern.log4j.Log4j2; diff --git a/src/main/java/com/getpcpanel/util/tray/win/TrayServiceWin.java b/src/main/java/com/getpcpanel/util/tray/win/TrayServiceWin.java index 4951442d..38cc5f65 100644 --- a/src/main/java/com/getpcpanel/util/tray/win/TrayServiceWin.java +++ b/src/main/java/com/getpcpanel/util/tray/win/TrayServiceWin.java @@ -6,9 +6,9 @@ import java.nio.ByteOrder; import com.getpcpanel.platform.WindowsBuild; -import com.getpcpanel.util.FileUtil; -import com.getpcpanel.util.OpenFolderEvent; -import com.getpcpanel.util.ShowMainEvent; +import com.getpcpanel.util.io.FileUtil; +import com.getpcpanel.util.app.OpenFolderEvent; +import com.getpcpanel.util.app.ShowMainEvent; import com.getpcpanel.util.tray.ITrayService; import com.getpcpanel.util.tray.awt.AwtTrayImpl; import com.sun.jna.Memory; diff --git a/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterConnectedEvent.java b/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterConnectedEvent.java deleted file mode 100644 index 579be86b..00000000 --- a/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterConnectedEvent.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.getpcpanel.voicemeeter; - -public record VoiceMeeterConnectedEvent() { -} diff --git a/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterDirtyEvent.java b/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterDirtyEvent.java deleted file mode 100644 index 99c7cc1d..00000000 --- a/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterDirtyEvent.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.getpcpanel.voicemeeter; - -public record VoiceMeeterDirtyEvent() { -} diff --git a/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterMuteEvent.java b/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterMuteEvent.java deleted file mode 100644 index 097b5eb6..00000000 --- a/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterMuteEvent.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.getpcpanel.voicemeeter; - -import com.getpcpanel.voicemeeter.Voicemeeter.ButtonType; -import com.getpcpanel.voicemeeter.Voicemeeter.ControlType; - -public record VoiceMeeterMuteEvent(ControlType ct, int idx, ButtonType button, boolean state) { -} diff --git a/src/main/java/dev/niels/wavelink/impl/model/WaveLinkImage.java b/src/main/java/dev/niels/wavelink/impl/model/WaveLinkImage.java index f727ba62..351cdec6 100644 --- a/src/main/java/dev/niels/wavelink/impl/model/WaveLinkImage.java +++ b/src/main/java/dev/niels/wavelink/impl/model/WaveLinkImage.java @@ -7,7 +7,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.getpcpanel.util.PngDecoder; +import com.getpcpanel.util.image.PngDecoder; import lombok.extern.log4j.Log4j2; diff --git a/src/main/resources/META-INF/native-image/com.getpcpanel/pcpanel/jni-config.json b/src/main/resources/META-INF/native-image/com.getpcpanel/pcpanel/jni-config.json index a84edc38..e3fbf7e6 100644 --- a/src/main/resources/META-INF/native-image/com.getpcpanel/pcpanel/jni-config.json +++ b/src/main/resources/META-INF/native-image/com.getpcpanel/pcpanel/jni-config.json @@ -1,24 +1,24 @@ [ { - "name": "com.getpcpanel.cpp.windows.SndCtrlWindows", + "name": "com.getpcpanel.integration.volume.platform.windows.SndCtrlWindows", "allDeclaredMethods": true, "allDeclaredConstructors": true, "allDeclaredFields": true }, { - "name": "com.getpcpanel.cpp.windows.WindowsAudioDevice", + "name": "com.getpcpanel.integration.volume.platform.windows.WindowsAudioDevice", "allDeclaredMethods": true, "allDeclaredConstructors": true, "allDeclaredFields": true }, { - "name": "com.getpcpanel.cpp.AudioDevice", + "name": "com.getpcpanel.integration.volume.platform.AudioDevice", "allDeclaredMethods": true, "allDeclaredConstructors": true, "allDeclaredFields": true }, { - "name": "com.getpcpanel.cpp.AudioSession", + "name": "com.getpcpanel.integration.volume.platform.AudioSession", "allDeclaredMethods": true, "allDeclaredConstructors": true, "allDeclaredFields": true diff --git a/src/main/resources/META-INF/native-image/com.getpcpanel/pcpanel/proxy-config.json b/src/main/resources/META-INF/native-image/com.getpcpanel/pcpanel/proxy-config.json index 8523df5c..44e28d06 100644 --- a/src/main/resources/META-INF/native-image/com.getpcpanel/pcpanel/proxy-config.json +++ b/src/main/resources/META-INF/native-image/com.getpcpanel/pcpanel/proxy-config.json @@ -3,7 +3,7 @@ {"interfaces": ["org.hid4java.jna.DarwinHidApiLibrary"]}, {"interfaces": ["org.hid4java.jna.HidrawHidApiLibrary"]}, {"interfaces": ["org.hid4java.jna.LibusbHidApiLibrary"]}, - {"interfaces": ["com.getpcpanel.voicemeeter.VoicemeeterInstance"]}, + {"interfaces": ["com.getpcpanel.integration.voicemeeter.VoicemeeterInstance"]}, {"interfaces": ["com.getpcpanel.iconextract.Shell32Extra"]}, {"interfaces": ["com.getpcpanel.util.tray.win.WinShell32"]}, {"interfaces": ["com.getpcpanel.util.tray.win.WinUser32Ext"]}, @@ -11,7 +11,7 @@ {"interfaces": ["com.getpcpanel.sleepdetection.Win32PowerNotify"]}, {"interfaces": ["com.getpcpanel.sleepdetection.LinuxX11$LibX11"]}, {"interfaces": ["com.getpcpanel.sleepdetection.LinuxX11$LibXext"]}, - {"interfaces": ["com.getpcpanel.util.Kernel32Console"]}, + {"interfaces": ["com.getpcpanel.util.os.Kernel32Console"]}, {"interfaces": ["com.sun.jna.platform.win32.Kernel32"]}, {"interfaces": ["com.sun.jna.platform.win32.User32"]}, {"interfaces": ["com.sun.jna.platform.win32.Shell32"]}, @@ -21,15 +21,15 @@ {"interfaces": ["com.getpcpanel.util.tray.wayland.StatusNotifierWatcher"]}, {"interfaces": ["com.getpcpanel.sleepdetection.Login1Manager"]}, {"interfaces": ["com.getpcpanel.sleepdetection.Login1Session"]}, - {"interfaces": ["com.getpcpanel.cpp.linux.MprisPlayer"]}, - {"interfaces": ["com.getpcpanel.overlay.KdeOsdService"]}, + {"interfaces": ["com.getpcpanel.integration.keyboard.platform.linux.MprisPlayer"]}, + {"interfaces": ["com.getpcpanel.integration.volume.overlay.KdeOsdService"]}, {"interfaces": ["org.freedesktop.dbus.interfaces.DBus"]}, {"interfaces": ["org.freedesktop.dbus.interfaces.Properties"]}, - {"interfaces": ["com.getpcpanel.cpp.osx.CoreAudioLib"]}, + {"interfaces": ["com.getpcpanel.integration.volume.platform.osx.CoreAudioLib"]}, {"interfaces": ["com.sun.jna.platform.mac.CoreFoundation"]}, - {"interfaces": ["com.getpcpanel.cpp.osx.OsxKeyboard$CoreGraphics"]}, - {"interfaces": ["com.getpcpanel.cpp.osx.OsxKeyboard$CoreFoundation"]}, - {"interfaces": ["com.getpcpanel.util.OsxPermissionHelper$ApplicationServicesLib"]}, - {"interfaces": ["com.getpcpanel.util.OsxPermissionHelper$IOKitLib"]} + {"interfaces": ["com.getpcpanel.integration.keyboard.platform.osx.OsxKeyboard$CoreGraphics"]}, + {"interfaces": ["com.getpcpanel.integration.keyboard.platform.osx.OsxKeyboard$CoreFoundation"]}, + {"interfaces": ["com.getpcpanel.util.os.OsxPermissionHelper$ApplicationServicesLib"]}, + {"interfaces": ["com.getpcpanel.util.os.OsxPermissionHelper$IOKitLib"]} ] diff --git a/src/main/resources/META-INF/native-image/com.getpcpanel/pcpanel/reflect-config.json b/src/main/resources/META-INF/native-image/com.getpcpanel/pcpanel/reflect-config.json index 44417600..4475b67a 100644 --- a/src/main/resources/META-INF/native-image/com.getpcpanel/pcpanel/reflect-config.json +++ b/src/main/resources/META-INF/native-image/com.getpcpanel/pcpanel/reflect-config.json @@ -43,11 +43,11 @@ "allDeclaredMethods": true }, { - "name": "com.getpcpanel.cpp.linux.MprisPlayer", + "name": "com.getpcpanel.integration.keyboard.platform.linux.MprisPlayer", "allDeclaredMethods": true }, { - "name": "com.getpcpanel.overlay.KdeOsdService", + "name": "com.getpcpanel.integration.volume.overlay.KdeOsdService", "allDeclaredMethods": true }, { diff --git a/src/main/resources/META-INF/native-image/reachability-metadata.json b/src/main/resources/META-INF/native-image/reachability-metadata.json index d74eb3bb..5f2bf823 100644 --- a/src/main/resources/META-INF/native-image/reachability-metadata.json +++ b/src/main/resources/META-INF/native-image/reachability-metadata.json @@ -134,7 +134,7 @@ "type": "com.getpcpanel.commands.PCPanelControlEvent" }, { - "type": "com.getpcpanel.mutecolor.MuteColorService" + "type": "com.getpcpanel.integration.volume.mutecolor.MuteColorService" }, { "type": "com.getpcpanel.commands.command.ButtonAction", @@ -149,7 +149,7 @@ "type": "com.getpcpanel.commands.command.Command" }, { - "type": "com.getpcpanel.commands.command.CommandBrightness", + "type": "com.getpcpanel.integration.device.command.CommandBrightness", "methods": [ { "name": "", @@ -164,7 +164,7 @@ ] }, { - "type": "com.getpcpanel.commands.command.CommandKeystroke", + "type": "com.getpcpanel.integration.keyboard.command.CommandKeystroke", "methods": [ { "name": "", @@ -179,10 +179,10 @@ ] }, { - "type": "com.getpcpanel.commands.command.CommandObs" + "type": "com.getpcpanel.integration.obs.command.CommandObs" }, { - "type": "com.getpcpanel.commands.command.CommandObsSetScene", + "type": "com.getpcpanel.integration.obs.command.CommandObsSetScene", "methods": [ { "name": "", @@ -197,7 +197,7 @@ ] }, { - "type": "com.getpcpanel.commands.command.CommandObsSetSourceVolume", + "type": "com.getpcpanel.integration.obs.command.CommandObsSetSourceVolume", "methods": [ { "name": "", @@ -217,17 +217,17 @@ ] }, { - "type": "com.getpcpanel.commands.command.CommandVoiceMeeter" + "type": "com.getpcpanel.integration.voicemeeter.command.CommandVoiceMeeter" }, { - "type": "com.getpcpanel.commands.command.CommandVoiceMeeterBasic", + "type": "com.getpcpanel.integration.voicemeeter.command.CommandVoiceMeeterBasic", "methods": [ { "name": "", "parameterTypes": [ - "com.getpcpanel.voicemeeter.Voicemeeter$ControlType", + "com.getpcpanel.integration.voicemeeter.Voicemeeter$ControlType", "int", - "com.getpcpanel.voicemeeter.Voicemeeter$DialType", + "com.getpcpanel.integration.voicemeeter.Voicemeeter$DialType", "com.getpcpanel.commands.command.DialAction$DialCommandParams" ] }, @@ -250,10 +250,10 @@ ] }, { - "type": "com.getpcpanel.commands.command.CommandVolume" + "type": "com.getpcpanel.integration.volume.command.CommandVolume" }, { - "type": "com.getpcpanel.commands.command.CommandVolumeDefaultDeviceAdvanced", + "type": "com.getpcpanel.integration.volume.command.CommandVolumeDefaultDeviceAdvanced", "methods": [ { "name": "getCommunicationPb", @@ -282,7 +282,7 @@ ] }, { - "type": "com.getpcpanel.commands.command.CommandVolumeDefaultDeviceAdvanced$CommandVolumeDefaultDeviceAdvancedBuilder", + "type": "com.getpcpanel.integration.volume.command.CommandVolumeDefaultDeviceAdvanced$CommandVolumeDefaultDeviceAdvancedBuilder", "methods": [ { "name": "", @@ -325,13 +325,13 @@ ] }, { - "type": "com.getpcpanel.commands.command.CommandVolumeDeviceMute", + "type": "com.getpcpanel.integration.volume.command.CommandVolumeDeviceMute", "methods": [ { "name": "", "parameterTypes": [ "java.lang.String", - "com.getpcpanel.cpp.MuteType" + "com.getpcpanel.integration.volume.platform.MuteType" ] }, { @@ -345,7 +345,7 @@ ] }, { - "type": "com.getpcpanel.commands.command.CommandVolumeFocus", + "type": "com.getpcpanel.integration.volume.command.CommandVolumeFocus", "methods": [ { "name": "", @@ -360,7 +360,7 @@ ] }, { - "type": "com.getpcpanel.commands.command.CommandVolumeProcess", + "type": "com.getpcpanel.integration.volume.command.CommandVolumeProcess", "fields": [ { "name": "unMuteOnVolumeChange" @@ -395,13 +395,13 @@ ] }, { - "type": "com.getpcpanel.commands.command.CommandVolumeProcessMute", + "type": "com.getpcpanel.integration.volume.command.CommandVolumeProcessMute", "methods": [ { "name": "", "parameterTypes": [ "java.util.Set", - "com.getpcpanel.cpp.MuteType" + "com.getpcpanel.integration.volume.platform.MuteType" ] }, { @@ -449,13 +449,13 @@ ] }, { - "type": "com.getpcpanel.cpp.AudioDevice" + "type": "com.getpcpanel.integration.volume.platform.AudioDevice" }, { - "type": "com.getpcpanel.cpp.AudioDeviceEvent" + "type": "com.getpcpanel.integration.volume.platform.AudioDeviceEvent" }, { - "type": "com.getpcpanel.cpp.AudioSession", + "type": "com.getpcpanel.integration.volume.platform.AudioSession", "jniAccessible": true, "methods": [ { @@ -473,25 +473,25 @@ ] }, { - "type": "com.getpcpanel.cpp.AudioSessionEvent" + "type": "com.getpcpanel.integration.volume.platform.AudioSessionEvent" }, { - "type": "com.getpcpanel.cpp.ISndCtrl" + "type": "com.getpcpanel.integration.volume.platform.ISndCtrl" }, { - "type": "com.getpcpanel.cpp.ISndCtrl$RunningApplication" + "type": "com.getpcpanel.integration.volume.platform.ISndCtrl$RunningApplication" }, { - "type": "com.getpcpanel.cpp.MuteType" + "type": "com.getpcpanel.integration.volume.platform.MuteType" }, { - "type": "com.getpcpanel.cpp.linux.LinuxKeyboard$X11" + "type": "com.getpcpanel.integration.keyboard.platform.linux.LinuxKeyboard$X11" }, { - "type": "com.getpcpanel.cpp.linux.LinuxKeyboard$XTest" + "type": "com.getpcpanel.integration.keyboard.platform.linux.LinuxKeyboard$XTest" }, { - "type": "com.getpcpanel.cpp.linux.pulseaudio.SndCtrlPulseAudioTest", + "type": "com.getpcpanel.integration.volume.platform.linux.SndCtrlPulseAudioTest", "methods": [ { "name": "", @@ -507,10 +507,10 @@ ] }, { - "type": "com.getpcpanel.cpp.windows.SndCtrlNative" + "type": "com.getpcpanel.integration.volume.platform.windows.SndCtrlNative" }, { - "type": "com.getpcpanel.cpp.windows.SndCtrlNativeConfigTest", + "type": "com.getpcpanel.integration.volume.platform.windows.SndCtrlNativeConfigTest", "methods": [ { "name": "", @@ -615,7 +615,7 @@ ] }, { - "type": "com.getpcpanel.cpp.windows.SndCtrlWindows", + "type": "com.getpcpanel.integration.volume.platform.windows.SndCtrlWindows", "jniAccessible": true, "methods": [ { @@ -645,10 +645,10 @@ ] }, { - "type": "com.getpcpanel.cpp.windows.WindowFocusChangedEvent" + "type": "com.getpcpanel.profile.WindowFocusChangedEvent" }, { - "type": "com.getpcpanel.cpp.windows.WindowsAudioDevice", + "type": "com.getpcpanel.integration.volume.platform.windows.WindowsAudioDevice", "jniAccessible": true, "methods": [ { @@ -666,43 +666,46 @@ ] }, { - "type": "com.getpcpanel.cpp.windows.WindowsAudioSession" + "type": "com.getpcpanel.integration.volume.platform.windows.WindowsAudioSession" }, { - "type": "com.getpcpanel.device.DeviceFactory" + "type": "com.getpcpanel.device.GenericDeviceFactory" + }, + { + "type": "com.getpcpanel.device.provider.pcpanel.PcPanelDeviceFactory" }, { "type": "com.getpcpanel.device.GlobalBrightnessChangedEvent" }, { - "type": "com.getpcpanel.hid.ButtonClickEvent" + "type": "com.getpcpanel.device.provider.pcpanel.ButtonClickEvent" }, { - "type": "com.getpcpanel.hid.DeviceCommunicationHandler$ButtonPressEvent" + "type": "com.getpcpanel.device.provider.pcpanel.DeviceCommunicationHandler$ButtonPressEvent" }, { - "type": "com.getpcpanel.hid.DeviceCommunicationHandler$KnobRotateEvent" + "type": "com.getpcpanel.device.provider.pcpanel.DeviceCommunicationHandler$KnobRotateEvent" }, { - "type": "com.getpcpanel.hid.DeviceCommunicationHandlerFactory" + "type": "com.getpcpanel.device.provider.pcpanel.DeviceCommunicationHandlerFactory" }, { - "type": "com.getpcpanel.hid.DeviceHolder" + "type": "com.getpcpanel.device.DeviceHolder" }, { - "type": "com.getpcpanel.hid.DeviceHolder$DeviceFullyConnectedEvent" + "type": "com.getpcpanel.device.DeviceHolder$DeviceFullyConnectedEvent" }, { - "type": "com.getpcpanel.hid.DeviceScanner" + "type": "com.getpcpanel.device.provider.pcpanel.DeviceScanner" }, { - "type": "com.getpcpanel.hid.DeviceScanner$DeviceConnectedEvent" + "type": "com.getpcpanel.device.provider.pcpanel.DeviceScanner$DeviceConnectedEvent" }, { - "type": "com.getpcpanel.hid.DeviceScanner$DeviceDisconnectedEvent" + "type": "com.getpcpanel.device.provider.pcpanel.DeviceScanner$DeviceDisconnectedEvent" }, { - "type": "com.getpcpanel.hid.DialValueCalculatorTest", + "type": "com.getpcpanel.commands.DialValueCalculatorTest", "methods": [ { "name": "", @@ -719,7 +722,7 @@ ] }, { - "type": "com.getpcpanel.hid.DialValueTest", + "type": "com.getpcpanel.commands.DialValueTest", "methods": [ { "name": "", @@ -739,10 +742,10 @@ ] }, { - "type": "com.getpcpanel.hid.InputInterpreter" + "type": "com.getpcpanel.device.provider.pcpanel.InputInterpreter" }, { - "type": "com.getpcpanel.hid.OutputInterpreter" + "type": "com.getpcpanel.device.provider.pcpanel.OutputInterpreter" }, { "type": "com.getpcpanel.iconextract.IIconService" @@ -801,16 +804,16 @@ "type": "com.getpcpanel.iconextract.Shell32Extra" }, { - "type": "com.getpcpanel.mqtt.MqttDeviceColorService" + "type": "com.getpcpanel.integration.mqtt.MqttDeviceColorService" }, { - "type": "com.getpcpanel.mqtt.MqttDeviceService" + "type": "com.getpcpanel.integration.mqtt.MqttDeviceService" }, { - "type": "com.getpcpanel.mqtt.MqttHomeAssistantHelper" + "type": "com.getpcpanel.integration.mqtt.MqttHomeAssistantHelper" }, { - "type": "com.getpcpanel.mqtt.MqttHomeAssistantHelper$HomeAssistantAvailability", + "type": "com.getpcpanel.integration.mqtt.MqttHomeAssistantHelper$HomeAssistantAvailability", "methods": [ { "name": "payload_available", @@ -831,7 +834,7 @@ ] }, { - "type": "com.getpcpanel.mqtt.MqttHomeAssistantHelper$HomeAssistantButtonConfig", + "type": "com.getpcpanel.integration.mqtt.MqttHomeAssistantHelper$HomeAssistantButtonConfig", "methods": [ { "name": "availability", @@ -876,7 +879,7 @@ ] }, { - "type": "com.getpcpanel.mqtt.MqttHomeAssistantHelper$HomeAssistantButtonEventConfig", + "type": "com.getpcpanel.integration.mqtt.MqttHomeAssistantHelper$HomeAssistantButtonEventConfig", "methods": [ { "name": "availability", @@ -917,7 +920,7 @@ ] }, { - "type": "com.getpcpanel.mqtt.MqttHomeAssistantHelper$HomeAssistantDevice", + "type": "com.getpcpanel.integration.mqtt.MqttHomeAssistantHelper$HomeAssistantDevice", "methods": [ { "name": "identifiers", @@ -950,7 +953,7 @@ ] }, { - "type": "com.getpcpanel.mqtt.MqttHomeAssistantHelper$HomeAssistantLightConfig", + "type": "com.getpcpanel.integration.mqtt.MqttHomeAssistantHelper$HomeAssistantLightConfig", "methods": [ { "name": "availability", @@ -1023,7 +1026,7 @@ ] }, { - "type": "com.getpcpanel.mqtt.MqttHomeAssistantHelper$HomeAssistantNumberConfig", + "type": "com.getpcpanel.integration.mqtt.MqttHomeAssistantHelper$HomeAssistantNumberConfig", "methods": [ { "name": "availability", @@ -1076,25 +1079,25 @@ ] }, { - "type": "com.getpcpanel.mqtt.MqttService" + "type": "com.getpcpanel.integration.mqtt.MqttService" }, { - "type": "com.getpcpanel.mqtt.MqttStatusEvent" + "type": "com.getpcpanel.integration.mqtt.MqttStatusEvent" }, { - "type": "com.getpcpanel.mqtt.MqttTopicHelper" + "type": "com.getpcpanel.integration.mqtt.MqttTopicHelper" }, { - "type": "com.getpcpanel.obs.OBS" + "type": "com.getpcpanel.integration.obs.OBS" }, { - "type": "com.getpcpanel.obs.OBSConnectEvent" + "type": "com.getpcpanel.integration.obs.OBSConnectEvent" }, { - "type": "com.getpcpanel.obs.OBSMuteEvent" + "type": "com.getpcpanel.integration.obs.OBSMuteEvent" }, { - "type": "com.getpcpanel.obs.OBS_ScheduledInvoker_reconnectIfNeeded_6f07f3246eb86b7d40ae832b0baf4e681d703711", + "type": "com.getpcpanel.integration.obs.OBS_ScheduledInvoker_reconnectIfNeeded_6f07f3246eb86b7d40ae832b0baf4e681d703711", "methods": [ { "name": "", @@ -1103,16 +1106,16 @@ ] }, { - "type": "com.getpcpanel.obs.ObsConnectedVolumeService" + "type": "com.getpcpanel.integration.obs.ObsConnectedVolumeService" }, { - "type": "com.getpcpanel.osc.OSCService" + "type": "com.getpcpanel.integration.osc.OSCService" }, { - "type": "com.getpcpanel.overlay.Overlay" + "type": "com.getpcpanel.integration.volume.overlay.Overlay" }, { - "type": "com.getpcpanel.overlay.VolumeOverlay", + "type": "com.getpcpanel.integration.volume.overlay.VolumeOverlay", "methods": [ { "name": "parseColor", @@ -1124,10 +1127,10 @@ ] }, { - "type": "com.getpcpanel.overlay.VolumeOverlay$OverlayPanel" + "type": "com.getpcpanel.integration.volume.overlay.VolumeOverlay$OverlayPanel" }, { - "type": "com.getpcpanel.overlay.VolumeOverlayNativeTest", + "type": "com.getpcpanel.integration.volume.overlay.VolumeOverlayNativeTest", "methods": [ { "name": "", @@ -1144,12 +1147,12 @@ ] }, { - "type": "com.getpcpanel.overlay.VolumeOverlayNativeTest$HexColors", + "type": "com.getpcpanel.integration.volume.overlay.VolumeOverlayNativeTest$HexColors", "methods": [ { "name": "", "parameterTypes": [ - "com.getpcpanel.overlay.VolumeOverlayNativeTest" + "com.getpcpanel.integration.volume.overlay.VolumeOverlayNativeTest" ] }, { @@ -1183,12 +1186,12 @@ ] }, { - "type": "com.getpcpanel.overlay.VolumeOverlayNativeTest$RgbColors", + "type": "com.getpcpanel.integration.volume.overlay.VolumeOverlayNativeTest$RgbColors", "methods": [ { "name": "", "parameterTypes": [ - "com.getpcpanel.overlay.VolumeOverlayNativeTest" + "com.getpcpanel.integration.volume.overlay.VolumeOverlayNativeTest" ] }, { @@ -1212,12 +1215,12 @@ ] }, { - "type": "com.getpcpanel.overlay.VolumeOverlayNativeTest$SwingClassLoading", + "type": "com.getpcpanel.integration.volume.overlay.VolumeOverlayNativeTest$SwingClassLoading", "methods": [ { "name": "", "parameterTypes": [ - "com.getpcpanel.overlay.VolumeOverlayNativeTest" + "com.getpcpanel.integration.volume.overlay.VolumeOverlayNativeTest" ] }, { @@ -1617,7 +1620,7 @@ { "name": "setMqtt", "parameterTypes": [ - "com.getpcpanel.profile.dto.MqttSettings" + "com.getpcpanel.integration.mqtt.dto.MqttSettings" ] }, { @@ -1701,7 +1704,7 @@ { "name": "setOverlayPosition", "parameterTypes": [ - "com.getpcpanel.profile.dto.OverlayPosition" + "com.getpcpanel.integration.volume.overlay.OverlayPosition" ] }, { @@ -1773,7 +1776,7 @@ { "name": "setWaveLink", "parameterTypes": [ - "com.getpcpanel.profile.dto.WaveLinkSettings" + "com.getpcpanel.integration.wavelink.dto.WaveLinkSettings" ] }, { @@ -1961,7 +1964,7 @@ "type": "com.getpcpanel.profile.dto.LightingConfig$LightingMode" }, { - "type": "com.getpcpanel.profile.dto.MqttSettings", + "type": "com.getpcpanel.integration.mqtt.dto.MqttSettings", "methods": [ { "name": "", @@ -1973,7 +1976,7 @@ "java.lang.String", "boolean", "java.lang.String", - "com.getpcpanel.profile.dto.MqttSettings$HomeAssistantSettings" + "com.getpcpanel.integration.mqtt.dto.MqttSettings$HomeAssistantSettings" ] }, { @@ -2011,7 +2014,7 @@ ] }, { - "type": "com.getpcpanel.profile.dto.MqttSettings$HomeAssistantSettings", + "type": "com.getpcpanel.integration.mqtt.dto.MqttSettings$HomeAssistantSettings", "methods": [ { "name": "", @@ -2036,7 +2039,7 @@ ] }, { - "type": "com.getpcpanel.profile.dto.OSCBinding", + "type": "com.getpcpanel.integration.osc.dto.OSCBinding", "methods": [ { "name": "", @@ -2066,7 +2069,7 @@ ] }, { - "type": "com.getpcpanel.profile.dto.OSCConnectionInfo", + "type": "com.getpcpanel.integration.osc.dto.OSCConnectionInfo", "methods": [ { "name": "", @@ -2086,7 +2089,7 @@ ] }, { - "type": "com.getpcpanel.profile.dto.OverlayPosition" + "type": "com.getpcpanel.integration.volume.overlay.OverlayPosition" }, { "type": "com.getpcpanel.profile.dto.SingleKnobLightingConfig", @@ -2336,7 +2339,7 @@ "type": "com.getpcpanel.profile.dto.SingleSliderLightingConfig[]" }, { - "type": "com.getpcpanel.profile.dto.WaveLinkSettings", + "type": "com.getpcpanel.integration.wavelink.dto.WaveLinkSettings", "methods": [ { "name": "", @@ -2351,19 +2354,10 @@ ] }, { - "type": "com.getpcpanel.rest.AudioResource" - }, - { - "type": "com.getpcpanel.rest.AudioResource$quarkusrestinvoker$listAudioDevices_550a3b0e13808220fb0b06709e729f8d6f308247", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ] + "type": "com.getpcpanel.integration.volume.AudioResource" }, { - "type": "com.getpcpanel.rest.AudioResource$quarkusrestinvoker$listAudioSessions_87b1ad592571c0490d5d65ae30900a3fd5c91858", + "type": "com.getpcpanel.integration.volume.AudioResource$quarkusrestinvoker$listAudioDevices_550a3b0e13808220fb0b06709e729f8d6f308247", "methods": [ { "name": "", @@ -2372,7 +2366,7 @@ ] }, { - "type": "com.getpcpanel.rest.AudioResource$quarkusrestinvoker$listInputDevices_486c01c1309f7e4a8b1b28fdee3458ff7823b6dc", + "type": "com.getpcpanel.integration.volume.AudioResource$quarkusrestinvoker$listAudioSessions_87b1ad592571c0490d5d65ae30900a3fd5c91858", "methods": [ { "name": "", @@ -2381,7 +2375,7 @@ ] }, { - "type": "com.getpcpanel.rest.AudioResource$quarkusrestinvoker$listOutputDevices_07238fa0093e377e9791afc81a93956fc9c90a1f", + "type": "com.getpcpanel.integration.volume.AudioResource$quarkusrestinvoker$listInputDevices_486c01c1309f7e4a8b1b28fdee3458ff7823b6dc", "methods": [ { "name": "", @@ -2390,7 +2384,7 @@ ] }, { - "type": "com.getpcpanel.rest.AudioResource$quarkusrestinvoker$listRunningApplications_111045ded5c591a7480f876665a40f8cc45b501b", + "type": "com.getpcpanel.integration.volume.AudioResource$quarkusrestinvoker$listOutputDevices_07238fa0093e377e9791afc81a93956fc9c90a1f", "methods": [ { "name": "", @@ -2399,10 +2393,7 @@ ] }, { - "type": "com.getpcpanel.rest.CommandsResource" - }, - { - "type": "com.getpcpanel.rest.CommandsResource$quarkusrestinvoker$listAvailableCommands_47312a0429306c144b67bec23b8273a7bf24ceec", + "type": "com.getpcpanel.integration.volume.AudioResource$quarkusrestinvoker$listRunningApplications_111045ded5c591a7480f876665a40f8cc45b501b", "methods": [ { "name": "", @@ -2411,10 +2402,10 @@ ] }, { - "type": "com.getpcpanel.rest.DeviceResource" + "type": "com.getpcpanel.device.rest.DeviceResource" }, { - "type": "com.getpcpanel.rest.DeviceResource$quarkusrestinvoker$createProfile_88e0b72b3cc218b89f781ee55504c22f31b2f27e", + "type": "com.getpcpanel.device.rest.DeviceResource$quarkusrestinvoker$createProfile_88e0b72b3cc218b89f781ee55504c22f31b2f27e", "methods": [ { "name": "", @@ -2423,7 +2414,7 @@ ] }, { - "type": "com.getpcpanel.rest.DeviceResource$quarkusrestinvoker$deleteProfile_9d32d4744c4970ae1371b95a7d547ee8fcfe1353", + "type": "com.getpcpanel.device.rest.DeviceResource$quarkusrestinvoker$deleteProfile_9d32d4744c4970ae1371b95a7d547ee8fcfe1353", "methods": [ { "name": "", @@ -2432,7 +2423,7 @@ ] }, { - "type": "com.getpcpanel.rest.DeviceResource$quarkusrestinvoker$getButton_95405a9e4345b0ab8a7d493bb1394d53861ed75c", + "type": "com.getpcpanel.device.rest.DeviceResource$quarkusrestinvoker$getButton_95405a9e4345b0ab8a7d493bb1394d53861ed75c", "methods": [ { "name": "", @@ -2441,7 +2432,7 @@ ] }, { - "type": "com.getpcpanel.rest.DeviceResource$quarkusrestinvoker$getDblButton_8107605d1ba06eba3e81dc77b2abf282fafd3126", + "type": "com.getpcpanel.device.rest.DeviceResource$quarkusrestinvoker$getDblButton_8107605d1ba06eba3e81dc77b2abf282fafd3126", "methods": [ { "name": "", @@ -2450,7 +2441,7 @@ ] }, { - "type": "com.getpcpanel.rest.DeviceResource$quarkusrestinvoker$getDevice_56060172aa7ec103e6a8d16bd0a0401de0b9b313", + "type": "com.getpcpanel.device.rest.DeviceResource$quarkusrestinvoker$getDevice_56060172aa7ec103e6a8d16bd0a0401de0b9b313", "methods": [ { "name": "", @@ -2459,7 +2450,7 @@ ] }, { - "type": "com.getpcpanel.rest.DeviceResource$quarkusrestinvoker$getDial_a0fce217793424f22cbe3a71a6a0155df3df1926", + "type": "com.getpcpanel.device.rest.DeviceResource$quarkusrestinvoker$getDial_a0fce217793424f22cbe3a71a6a0155df3df1926", "methods": [ { "name": "", @@ -2468,7 +2459,7 @@ ] }, { - "type": "com.getpcpanel.rest.DeviceResource$quarkusrestinvoker$getKnobSettings_700328b65fa1fcb1835f30fb00f2ad16c22f5f18", + "type": "com.getpcpanel.device.rest.DeviceResource$quarkusrestinvoker$getKnobSettings_700328b65fa1fcb1835f30fb00f2ad16c22f5f18", "methods": [ { "name": "", @@ -2477,7 +2468,7 @@ ] }, { - "type": "com.getpcpanel.rest.DeviceResource$quarkusrestinvoker$getLighting_91993aac0e4a5b7c8e8be4b52f360dacee3ffcba", + "type": "com.getpcpanel.device.rest.DeviceResource$quarkusrestinvoker$getLighting_91993aac0e4a5b7c8e8be4b52f360dacee3ffcba", "methods": [ { "name": "", @@ -2486,7 +2477,7 @@ ] }, { - "type": "com.getpcpanel.rest.DeviceResource$quarkusrestinvoker$listDevices_e219037e4a3399072bd6f450ff430ee7f67d25e8", + "type": "com.getpcpanel.device.rest.DeviceResource$quarkusrestinvoker$listDevices_e219037e4a3399072bd6f450ff430ee7f67d25e8", "methods": [ { "name": "", @@ -2495,7 +2486,7 @@ ] }, { - "type": "com.getpcpanel.rest.DeviceResource$quarkusrestinvoker$listProfiles_0ca917159424d2b8a9cf788ec1185aedc089d542", + "type": "com.getpcpanel.device.rest.DeviceResource$quarkusrestinvoker$listProfiles_0ca917159424d2b8a9cf788ec1185aedc089d542", "methods": [ { "name": "", @@ -2504,7 +2495,7 @@ ] }, { - "type": "com.getpcpanel.rest.DeviceResource$quarkusrestinvoker$renameDevice_c12a7298412f8af1081cf456a541fb61c5c7ee8c", + "type": "com.getpcpanel.device.rest.DeviceResource$quarkusrestinvoker$renameDevice_c12a7298412f8af1081cf456a541fb61c5c7ee8c", "methods": [ { "name": "", @@ -2513,7 +2504,7 @@ ] }, { - "type": "com.getpcpanel.rest.DeviceResource$quarkusrestinvoker$setButton_eed2abfcf7f93ce2c2b24fa5eb23de3ee9b767a9", + "type": "com.getpcpanel.device.rest.DeviceResource$quarkusrestinvoker$setButton_eed2abfcf7f93ce2c2b24fa5eb23de3ee9b767a9", "methods": [ { "name": "", @@ -2522,7 +2513,7 @@ ] }, { - "type": "com.getpcpanel.rest.DeviceResource$quarkusrestinvoker$setControlAssignments_c562b43459bc0e2fd723850281361ef3c6d7eb93", + "type": "com.getpcpanel.device.rest.DeviceResource$quarkusrestinvoker$setControlAssignments_c562b43459bc0e2fd723850281361ef3c6d7eb93", "methods": [ { "name": "", @@ -2531,7 +2522,7 @@ ] }, { - "type": "com.getpcpanel.rest.DeviceResource$quarkusrestinvoker$setDblButton_a0df09678683d42577657cf1a090f7f405067e2c", + "type": "com.getpcpanel.device.rest.DeviceResource$quarkusrestinvoker$setDblButton_a0df09678683d42577657cf1a090f7f405067e2c", "methods": [ { "name": "", @@ -2540,7 +2531,7 @@ ] }, { - "type": "com.getpcpanel.rest.DeviceResource$quarkusrestinvoker$setDial_f682a143c7e3ef0796d1547515908e05db60a875", + "type": "com.getpcpanel.device.rest.DeviceResource$quarkusrestinvoker$setDial_f682a143c7e3ef0796d1547515908e05db60a875", "methods": [ { "name": "", @@ -2549,7 +2540,7 @@ ] }, { - "type": "com.getpcpanel.rest.DeviceResource$quarkusrestinvoker$setKnobSettings_21aec8f7c847c04e9dfb6a9bca9e62fe39e2d1ae", + "type": "com.getpcpanel.device.rest.DeviceResource$quarkusrestinvoker$setKnobSettings_21aec8f7c847c04e9dfb6a9bca9e62fe39e2d1ae", "methods": [ { "name": "", @@ -2558,7 +2549,7 @@ ] }, { - "type": "com.getpcpanel.rest.DeviceResource$quarkusrestinvoker$setLighting_f66173d4ddfbe8acfdf22bcb19769100485e9dbf", + "type": "com.getpcpanel.device.rest.DeviceResource$quarkusrestinvoker$setLighting_f66173d4ddfbe8acfdf22bcb19769100485e9dbf", "methods": [ { "name": "", @@ -2567,7 +2558,7 @@ ] }, { - "type": "com.getpcpanel.rest.DeviceResource$quarkusrestinvoker$switchProfile_02ac5ffc5ddabd13031116edd28722ff686e677d", + "type": "com.getpcpanel.device.rest.DeviceResource$quarkusrestinvoker$switchProfile_02ac5ffc5ddabd13031116edd28722ff686e677d", "methods": [ { "name": "", @@ -2624,10 +2615,10 @@ ] }, { - "type": "com.getpcpanel.rest.ObsResource" + "type": "com.getpcpanel.integration.obs.rest.ObsResource" }, { - "type": "com.getpcpanel.rest.ObsResource$quarkusrestinvoker$listScenes_d45e7a31126e58273b41c15802da4944d2079682", + "type": "com.getpcpanel.integration.obs.rest.ObsResource$quarkusrestinvoker$listScenes_d45e7a31126e58273b41c15802da4944d2079682", "methods": [ { "name": "", @@ -2636,7 +2627,7 @@ ] }, { - "type": "com.getpcpanel.rest.ObsResource$quarkusrestinvoker$listSources_5f705776229abc7f3484fe8cfadec2344a09af33", + "type": "com.getpcpanel.integration.obs.rest.ObsResource$quarkusrestinvoker$listSources_5f705776229abc7f3484fe8cfadec2344a09af33", "methods": [ { "name": "", @@ -2645,13 +2636,13 @@ ] }, { - "type": "com.getpcpanel.rest.OverlayResource" + "type": "com.getpcpanel.integration.volume.OverlayResource" }, { - "type": "com.getpcpanel.rest.OverlayResource$OverlayDto" + "type": "com.getpcpanel.integration.volume.OverlayResource$OverlayDto" }, { - "type": "com.getpcpanel.rest.OverlayResource$quarkusrestinvoker$showOverlay_53c93d1310f135ffd7c8e51be7eb1f6ba4eac4ba", + "type": "com.getpcpanel.integration.volume.OverlayResource$quarkusrestinvoker$showOverlay_53c93d1310f135ffd7c8e51be7eb1f6ba4eac4ba", "methods": [ { "name": "", @@ -2660,7 +2651,7 @@ ] }, { - "type": "com.getpcpanel.rest.OverlayResource$quarkusrestinvoker$testOverlay_25269a521d728b138ab48fc9e9ade9c8f2dfd2fc", + "type": "com.getpcpanel.integration.volume.OverlayResource$quarkusrestinvoker$testOverlay_25269a521d728b138ab48fc9e9ade9c8f2dfd2fc", "methods": [ { "name": "", @@ -2669,10 +2660,10 @@ ] }, { - "type": "com.getpcpanel.rest.ProVisualColorsService" + "type": "com.getpcpanel.device.provider.pcpanel.ProVisualColorsService" }, { - "type": "com.getpcpanel.rest.ProVisualColorsServiceTest", + "type": "com.getpcpanel.device.provider.pcpanel.ProVisualColorsServiceTest", "methods": [ { "name": "", @@ -2754,13 +2745,13 @@ ] }, { - "type": "com.getpcpanel.rest.VoiceMeeterResource" + "type": "com.getpcpanel.integration.voicemeeter.rest.VoiceMeeterResource" }, { - "type": "com.getpcpanel.rest.VoiceMeeterResource$VoiceMeeterParam" + "type": "com.getpcpanel.integration.voicemeeter.rest.VoiceMeeterResource$VoiceMeeterParam" }, { - "type": "com.getpcpanel.rest.VoiceMeeterResource$quarkusrestinvoker$getAdvancedParams_789ebbb7df6501274ae825c6a27179c302791436", + "type": "com.getpcpanel.integration.voicemeeter.rest.VoiceMeeterResource$quarkusrestinvoker$getAdvancedParams_789ebbb7df6501274ae825c6a27179c302791436", "methods": [ { "name": "", @@ -2769,7 +2760,7 @@ ] }, { - "type": "com.getpcpanel.rest.VoiceMeeterResource$quarkusrestinvoker$getBasicParams_3fb05518ea0ad168b8b8c6bcdb89e050368544a7", + "type": "com.getpcpanel.integration.voicemeeter.rest.VoiceMeeterResource$quarkusrestinvoker$getBasicParams_3fb05518ea0ad168b8b8c6bcdb89e050368544a7", "methods": [ { "name": "", @@ -2777,9 +2768,6 @@ } ] }, - { - "type": "com.getpcpanel.rest.model.dto.CommandType" - }, { "type": "com.getpcpanel.rest.model.dto.ControlAssignmentsUpdateDto" }, @@ -3104,7 +3092,7 @@ { "name": "setOverlayPosition", "parameterTypes": [ - "com.getpcpanel.profile.dto.OverlayPosition" + "com.getpcpanel.integration.volume.overlay.OverlayPosition" ] }, { @@ -3211,10 +3199,10 @@ ] }, { - "type": "com.getpcpanel.rest.wavelink.WaveLinkResource" + "type": "com.getpcpanel.integration.wavelink.rest.WaveLinkResource" }, { - "type": "com.getpcpanel.rest.wavelink.WaveLinkResource$quarkusrestinvoker$listAllDevices_98920131f1bddff6dc628ac5409344fdbe93b63b", + "type": "com.getpcpanel.integration.wavelink.rest.WaveLinkResource$quarkusrestinvoker$listAllDevices_98920131f1bddff6dc628ac5409344fdbe93b63b", "methods": [ { "name": "", @@ -3223,10 +3211,10 @@ ] }, { - "type": "com.getpcpanel.rest.wavelink.dto.WaveLinkMixDto[]" + "type": "com.getpcpanel.integration.wavelink.rest.dto.WaveLinkMixDto[]" }, { - "type": "com.getpcpanel.rest.wavelink.dto.WaveLinkResponseDto" + "type": "com.getpcpanel.integration.wavelink.rest.dto.WaveLinkResponseDto" }, { "type": "com.getpcpanel.sleepdetection.SleepDetector" @@ -3238,34 +3226,34 @@ "type": "com.getpcpanel.sleepdetection.WindowsSystemEventService" }, { - "type": "com.getpcpanel.util.AppEvents" + "type": "com.getpcpanel.util.app.AppEvents" }, { - "type": "com.getpcpanel.util.AppShutdownState" + "type": "com.getpcpanel.util.app.AppShutdownState" }, { "type": "com.getpcpanel.util.CdiHelper" }, { - "type": "com.getpcpanel.util.Debouncer" + "type": "com.getpcpanel.util.concurrent.Debouncer" }, { - "type": "com.getpcpanel.util.ExtractUtil" + "type": "com.getpcpanel.util.io.ExtractUtil" }, { - "type": "com.getpcpanel.util.FileUtil" + "type": "com.getpcpanel.util.io.FileUtil" }, { - "type": "com.getpcpanel.util.IPlatformCommand" + "type": "com.getpcpanel.integration.program.IPlatformCommand" }, { - "type": "com.getpcpanel.util.IPlatformCommand$WindowsPlatformCommand" + "type": "com.getpcpanel.integration.program.IPlatformCommand$WindowsPlatformCommand" }, { - "type": "com.getpcpanel.util.ProcessHelper" + "type": "com.getpcpanel.util.os.ProcessHelper" }, { - "type": "com.getpcpanel.util.ShortcutHook" + "type": "com.getpcpanel.profile.ShortcutHook" }, { "type": "com.getpcpanel.util.coloroverride.IOverrideColorProvider" @@ -3299,34 +3287,34 @@ ] }, { - "type": "com.getpcpanel.voicemeeter.VoiceMeeterConnectedEvent" + "type": "com.getpcpanel.integration.voicemeeter.VoiceMeeterConnectedEvent" }, { - "type": "com.getpcpanel.voicemeeter.VoiceMeeterConnectedVolumeService" + "type": "com.getpcpanel.integration.voicemeeter.VoiceMeeterConnectedVolumeService" }, { - "type": "com.getpcpanel.voicemeeter.VoiceMeeterMuteEvent" + "type": "com.getpcpanel.integration.voicemeeter.VoiceMeeterMuteEvent" }, { - "type": "com.getpcpanel.voicemeeter.VoiceMeeterMuteService" + "type": "com.getpcpanel.integration.voicemeeter.VoiceMeeterMuteService" }, { - "type": "com.getpcpanel.voicemeeter.Voicemeeter" + "type": "com.getpcpanel.integration.voicemeeter.Voicemeeter" }, { - "type": "com.getpcpanel.voicemeeter.Voicemeeter$ControlType" + "type": "com.getpcpanel.integration.voicemeeter.Voicemeeter$ControlType" }, { - "type": "com.getpcpanel.voicemeeter.Voicemeeter$DialType" + "type": "com.getpcpanel.integration.voicemeeter.Voicemeeter$DialType" }, { - "type": "com.getpcpanel.voicemeeter.VoicemeeterAPI" + "type": "com.getpcpanel.integration.voicemeeter.VoicemeeterAPI" }, { - "type": "com.getpcpanel.voicemeeter.VoicemeeterInstance" + "type": "com.getpcpanel.integration.voicemeeter.VoicemeeterInstance" }, { - "type": "com.getpcpanel.voicemeeter.VoicemeeterInstance$tagVBVMR_AUDIOBUFFER", + "type": "com.getpcpanel.integration.voicemeeter.VoicemeeterInstance$tagVBVMR_AUDIOBUFFER", "fields": [ { "name": "audiobuffer_nbi" @@ -3349,7 +3337,7 @@ ] }, { - "type": "com.getpcpanel.voicemeeter.VoicemeeterInstance$tagVBVMR_AUDIOINFO", + "type": "com.getpcpanel.integration.voicemeeter.VoicemeeterInstance$tagVBVMR_AUDIOINFO", "fields": [ { "name": "nbSamplePerFrame" @@ -3360,7 +3348,7 @@ ] }, { - "type": "com.getpcpanel.voicemeeter.VoicemeeterInstance$tagVBVMR_INTERFACE", + "type": "com.getpcpanel.integration.voicemeeter.VoicemeeterInstance$tagVBVMR_INTERFACE", "fields": [ { "name": "VBVMR_AudioCallbackRegister" @@ -3443,7 +3431,7 @@ ] }, { - "type": "com.getpcpanel.voicemeeter.VoicemeeterNativeTest", + "type": "com.getpcpanel.integration.voicemeeter.VoicemeeterNativeTest", "methods": [ { "name": "", @@ -3508,7 +3496,7 @@ ] }, { - "type": "com.getpcpanel.voicemeeter.Voicemeeter_ScheduledInvoker_checkParamsDirtyScheduled_0347d99f171a2b21b9c9adf917b46e141dc1b212", + "type": "com.getpcpanel.integration.voicemeeter.Voicemeeter_ScheduledInvoker_checkParamsDirtyScheduled_0347d99f171a2b21b9c9adf917b46e141dc1b212", "methods": [ { "name": "", @@ -3517,16 +3505,16 @@ ] }, { - "type": "com.getpcpanel.volume.VolumeCoordinatorService" + "type": "com.getpcpanel.integration.volume.VolumeCoordinatorService" }, { - "type": "com.getpcpanel.wavelink.WaveLinkIconHandler" + "type": "com.getpcpanel.integration.wavelink.WaveLinkIconHandler" }, { - "type": "com.getpcpanel.wavelink.WaveLinkService" + "type": "com.getpcpanel.integration.wavelink.WaveLinkService" }, { - "type": "com.getpcpanel.wavelink.WaveLinkService_ScheduledInvoker_checkConnection_1196ea9e6f0adeb1c2a90be981b980a4df038c55", + "type": "com.getpcpanel.integration.wavelink.WaveLinkService_ScheduledInvoker_checkConnection_1196ea9e6f0adeb1c2a90be981b980a4df038c55", "methods": [ { "name": "", @@ -3535,10 +3523,10 @@ ] }, { - "type": "com.getpcpanel.wavelink.command.CommandWaveLink" + "type": "com.getpcpanel.integration.wavelink.command.CommandWaveLink" }, { - "type": "com.getpcpanel.wavelink.command.CommandWaveLinkChange", + "type": "com.getpcpanel.integration.wavelink.command.CommandWaveLinkChange", "methods": [ { "name": "getCommandType", @@ -3555,12 +3543,12 @@ ] }, { - "type": "com.getpcpanel.wavelink.command.CommandWaveLinkChangeLevel", + "type": "com.getpcpanel.integration.wavelink.command.CommandWaveLinkChangeLevel", "methods": [ { "name": "", "parameterTypes": [ - "com.getpcpanel.wavelink.command.WaveLinkCommandTarget", + "com.getpcpanel.integration.wavelink.command.WaveLinkCommandTarget", "java.lang.String", "java.lang.String", "com.getpcpanel.commands.command.DialAction$DialCommandParams" @@ -3573,7 +3561,7 @@ ] }, { - "type": "com.getpcpanel.wavelink.command.CommandWaveLinkChannelEffect", + "type": "com.getpcpanel.integration.wavelink.command.CommandWaveLinkChannelEffect", "methods": [ { "name": "", @@ -3582,7 +3570,7 @@ "java.lang.String", "java.lang.String", "java.lang.String", - "com.getpcpanel.cpp.MuteType" + "com.getpcpanel.integration.volume.platform.MuteType" ] }, { @@ -3608,7 +3596,7 @@ ] }, { - "type": "com.getpcpanel.wavelink.command.WaveLinkCommandTarget" + "type": "com.getpcpanel.integration.wavelink.command.WaveLinkCommandTarget" }, { "type": "com.github.benmanes.caffeine.cache.UnboundedLocalCache", @@ -11489,14 +11477,14 @@ { "type": { "proxy": [ - "com.getpcpanel.cpp.linux.LinuxKeyboard$X11" + "com.getpcpanel.integration.keyboard.platform.linux.LinuxKeyboard$X11" ] } }, { "type": { "proxy": [ - "com.getpcpanel.cpp.linux.LinuxKeyboard$XTest" + "com.getpcpanel.integration.keyboard.platform.linux.LinuxKeyboard$XTest" ] } }, @@ -11791,4 +11779,4 @@ "bundle": "sun.awt.resources.awt" } ] -} \ No newline at end of file +} diff --git a/src/main/resources/SndCtrl.dll b/src/main/resources/SndCtrl.dll old mode 100644 new mode 100755 index 67624aa8..678bdd4a Binary files a/src/main/resources/SndCtrl.dll and b/src/main/resources/SndCtrl.dll differ diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 48cfcc97..059a41c5 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -97,7 +97,7 @@ linux.commands.kdotool=kdotool # Use **/* glob to match all nested paths. quarkus.native.resources.includes=SndCtrl.dll,**/*.dll,**/*.so,**/*.dylib,**/*.jnilib,assets/**,org/eclipse/paho/**,META-INF/services/org.eclipse.paho.mqttv5.client.spi.NetworkModuleFactory,META-INF/services/javax.sound.midi.spi.MidiDeviceProvider -# Platform init: JNA, hid4java, and AWT (KeyMacro) must initialise at run time, not build time. +# Platform init: JNA, hid4java, and AWT must initialise at run time, not build time. # reflect-config.json lives under META-INF/native-image/ on the classpath and is auto-discovered. # Each comma is a list-item separator — each item becomes a separate native-image argument. # @@ -115,16 +115,16 @@ quarkus.native.resources.includes=SndCtrl.dll,**/*.dll,**/*.so,**/*.dylib,**/*.j quarkus.native.additional-build-args=\ -Os,\ -R:MaxHeapSize=67108864,\ - --initialize-at-run-time=com.getpcpanel.util.Kernel32Console,\ + --initialize-at-run-time=com.getpcpanel.util.os.Kernel32Console,\ -J-XX:-UseCompressedOops,\ --initialize-at-run-time=org.hid4java,\ --initialize-at-run-time=com.fazecast.jSerialComm,\ --initialize-at-run-time=com.sun.media.sound,\ --initialize-at-run-time=javax.sound.midi.MidiSystem,\ - --initialize-at-run-time=com.getpcpanel.commands.KeyMacro,\ - --initialize-at-run-time=com.getpcpanel.cpp.linux.LinuxKeyboard$X11,\ - --initialize-at-run-time=com.getpcpanel.cpp.linux.LinuxKeyboard$XTest,\ - --initialize-at-run-time=com.getpcpanel.overlay.OverlayRenderer,\ + \ + --initialize-at-run-time=com.getpcpanel.integration.keyboard.platform.linux.LinuxKeyboard$X11,\ + --initialize-at-run-time=com.getpcpanel.integration.keyboard.platform.linux.LinuxKeyboard$XTest,\ + --initialize-at-run-time=com.getpcpanel.integration.volume.overlay.OverlayRenderer,\ --initialize-at-run-time=org.freedesktop.dbus,\ --trace-object-instantiation=java.security.SecureRandom,\ --initialize-at-run-time=com.sun.jna,\ @@ -137,15 +137,15 @@ quarkus.native.additional-build-args=\ --initialize-at-run-time=com.getpcpanel.sleepdetection.WindowsPowerEventMonitor,\ --initialize-at-run-time=com.getpcpanel.sleepdetection.LinuxX11$LibX11,\ --initialize-at-run-time=com.getpcpanel.sleepdetection.LinuxX11$LibXext,\ - --initialize-at-run-time=com.getpcpanel.cpp.osx.CoreAudioLib,\ - --initialize-at-run-time=com.getpcpanel.cpp.osx.CoreAudioLib$AudioObjectPropertyAddress,\ - --initialize-at-run-time=com.getpcpanel.cpp.osx.CoreAudioLib$AudioObjectPropertyListenerProc,\ - --initialize-at-run-time=com.getpcpanel.cpp.osx.OsxKeyboard$CoreGraphics,\ - --initialize-at-run-time=com.getpcpanel.cpp.osx.OsxKeyboard$CoreFoundation,\ - --initialize-at-run-time=com.getpcpanel.util.OsxPermissionHelper,\ - --initialize-at-run-time=com.getpcpanel.util.OsxPermissionHelper$ApplicationServicesLib,\ - --initialize-at-run-time=com.getpcpanel.util.OsxPermissionHelper$IOKitLib,\ - --initialize-at-run-time=com.getpcpanel.voicemeeter.VoicemeeterInstance,\ - --initialize-at-run-time=com.getpcpanel.voicemeeter.VoicemeeterInstance$tagVBVMR_AUDIOINFO,\ - --initialize-at-run-time=com.getpcpanel.voicemeeter.VoicemeeterInstance$tagVBVMR_AUDIOBUFFER,\ - --initialize-at-run-time=com.getpcpanel.voicemeeter.VoicemeeterInstance$tagVBVMR_INTERFACE${native.awt.args}${native.platform.linker.args} + --initialize-at-run-time=com.getpcpanel.integration.volume.platform.osx.CoreAudioLib,\ + --initialize-at-run-time=com.getpcpanel.integration.volume.platform.osx.CoreAudioLib$AudioObjectPropertyAddress,\ + --initialize-at-run-time=com.getpcpanel.integration.volume.platform.osx.CoreAudioLib$AudioObjectPropertyListenerProc,\ + --initialize-at-run-time=com.getpcpanel.integration.keyboard.platform.osx.OsxKeyboard$CoreGraphics,\ + --initialize-at-run-time=com.getpcpanel.integration.keyboard.platform.osx.OsxKeyboard$CoreFoundation,\ + --initialize-at-run-time=com.getpcpanel.util.os.OsxPermissionHelper,\ + --initialize-at-run-time=com.getpcpanel.util.os.OsxPermissionHelper$ApplicationServicesLib,\ + --initialize-at-run-time=com.getpcpanel.util.os.OsxPermissionHelper$IOKitLib,\ + --initialize-at-run-time=com.getpcpanel.integration.voicemeeter.VoicemeeterInstance,\ + --initialize-at-run-time=com.getpcpanel.integration.voicemeeter.VoicemeeterInstance$tagVBVMR_AUDIOINFO,\ + --initialize-at-run-time=com.getpcpanel.integration.voicemeeter.VoicemeeterInstance$tagVBVMR_AUDIOBUFFER,\ + --initialize-at-run-time=com.getpcpanel.integration.voicemeeter.VoicemeeterInstance$tagVBVMR_INTERFACE${native.awt.args}${native.platform.linker.args} diff --git a/src/main/webui/src/app/features/commands/command-catalog.ts b/src/main/webui/src/app/features/commands/command-catalog.ts index 91d5be34..4dbdd582 100644 --- a/src/main/webui/src/app/features/commands/command-catalog.ts +++ b/src/main/webui/src/app/features/commands/command-catalog.ts @@ -1,4 +1,5 @@ import { IconName } from '../../ui'; +import { GENERATED_COMMANDS } from './command-registry.generated'; /** * Data-driven catalog of every assignable command. One generic editor renders a @@ -56,10 +57,11 @@ const MUTE_OPTS = [ ]; const dialParams = () => ({ invert: false, moveStart: 0, moveEnd: 0 }); -export const COMMANDS: CommandDef[] = [ +interface FieldDef_ { type: string; buildEmpty: () => Record; fields: FieldDef[]; } +const FIELD_DEFS: FieldDef_[] = [ // ── AUDIO ──────────────────────────────────────────────────────────────── { - type: P + 'CommandVolumeProcess', label: 'App volume', category: 'audio', kinds: ['dial'], icon: 'volume', + type: P + 'CommandVolumeProcess', buildEmpty: () => ({ _type: P + 'CommandVolumeProcess', device: '', processName: [], unMuteOnVolumeChange: false, dialParams: dialParams(), invert: false }), fields: [ { kind: 'apps', key: 'processName', label: 'Applications' }, @@ -67,7 +69,7 @@ export const COMMANDS: CommandDef[] = [ ], }, { - type: P + 'CommandVolumeProcessMute', label: 'App mute', category: 'audio', kinds: ['button'], icon: 'volume-x', + type: P + 'CommandVolumeProcessMute', buildEmpty: () => ({ _type: P + 'CommandVolumeProcessMute', muteType: 'toggle', processName: [], overlayText: '' }), fields: [ { kind: 'apps', key: 'processName', label: 'Applications' }, @@ -75,17 +77,17 @@ export const COMMANDS: CommandDef[] = [ ], }, { - type: P + 'CommandVolumeFocus', label: 'Focused-app volume', category: 'audio', kinds: ['dial'], icon: 'volume', + type: P + 'CommandVolumeFocus', buildEmpty: () => ({ _type: P + 'CommandVolumeFocus', dialParams: dialParams(), invert: false }), fields: [], }, { - type: P + 'CommandVolumeFocusMute', label: 'Focused-app mute', category: 'audio', kinds: ['button'], icon: 'volume-x', + type: P + 'CommandVolumeFocusMute', buildEmpty: () => ({ _type: P + 'CommandVolumeFocusMute', muteType: 'toggle', overlayText: '' }), fields: [{ kind: 'mute', key: 'muteType', label: 'Action' }], }, { - type: P + 'CommandVolumeDevice', label: 'Device volume', category: 'audio', kinds: ['dial'], icon: 'volume', + type: P + 'CommandVolumeDevice', buildEmpty: () => ({ _type: P + 'CommandVolumeDevice', deviceId: '', unMuteOnVolumeChange: false, dialParams: dialParams(), invert: false }), fields: [ { kind: 'device', key: 'deviceId', label: 'Audio device', filter: 'all', defaultLabel: 'Default device' }, @@ -93,7 +95,7 @@ export const COMMANDS: CommandDef[] = [ ], }, { - type: P + 'CommandVolumeDeviceMute', label: 'Device mute', category: 'audio', kinds: ['button'], icon: 'volume-x', + type: P + 'CommandVolumeDeviceMute', buildEmpty: () => ({ _type: P + 'CommandVolumeDeviceMute', deviceId: '', muteType: 'toggle', overlayText: '' }), fields: [ { kind: 'device', key: 'deviceId', label: 'Audio device', filter: 'all', defaultLabel: 'Default device' }, @@ -101,17 +103,17 @@ export const COMMANDS: CommandDef[] = [ ], }, { - type: P + 'CommandVolumeDefaultDevice', label: 'Set default device', category: 'audio', kinds: ['button'], icon: 'monitor', + type: P + 'CommandVolumeDefaultDevice', buildEmpty: () => ({ _type: P + 'CommandVolumeDefaultDevice', deviceId: '', overlayText: '' }), fields: [{ kind: 'device', key: 'deviceId', label: 'Default device', filter: 'all' }], }, { - type: P + 'CommandVolumeDefaultDeviceToggle', label: 'Cycle default device', category: 'audio', kinds: ['button'], icon: 'refresh', + type: P + 'CommandVolumeDefaultDeviceToggle', buildEmpty: () => ({ _type: P + 'CommandVolumeDefaultDeviceToggle', currentIdx: 0, devices: [], overlayText: '' }), fields: [{ kind: 'devices-list', key: 'devices', label: 'Devices to cycle' }], }, { - type: P + 'CommandVolumeDefaultDeviceAdvanced', label: 'Advanced default device', category: 'audio', kinds: ['button'], icon: 'monitor', + type: P + 'CommandVolumeDefaultDeviceAdvanced', buildEmpty: () => ({ _type: P + 'CommandVolumeDefaultDeviceAdvanced', communicationPb: '', communicationRec: '', mediaPb: '', mediaRec: '', name: '', overlayText: '' }), fields: [ { kind: 'device', key: 'mediaPb', label: 'Media — playback', filter: 'output' }, @@ -123,32 +125,32 @@ export const COMMANDS: CommandDef[] = [ // ── DEVICE & SYSTEM ──────────────────────────────────────────────────────── { - type: P + 'CommandBrightness', label: 'Brightness', category: 'system', kinds: ['dial'], icon: 'sun', + type: P + 'CommandBrightness', buildEmpty: () => ({ _type: P + 'CommandBrightness', dialParams: dialParams(), invert: false }), fields: [], }, { - type: P + 'CommandProfile', label: 'Switch profile', category: 'system', kinds: ['button'], icon: 'refresh', + type: P + 'CommandProfile', buildEmpty: () => ({ _type: P + 'CommandProfile', profile: '' }), fields: [{ kind: 'select-live', key: 'profile', label: 'Profile', source: 'profiles' }], }, { - type: P + 'CommandAnalogBands', label: 'Stepped switch (ranges)', category: 'system', kinds: ['dial'], icon: 'sliders', + type: P + 'CommandAnalogBands', buildEmpty: () => ({ _type: P + 'CommandAnalogBands', bands: [] }), fields: [{ kind: 'analog-bands' }], }, { - type: P + 'CommandRun', label: 'Run command', category: 'system', kinds: ['button'], icon: 'zap', + type: P + 'CommandRun', buildEmpty: () => ({ _type: P + 'CommandRun', command: '', overlayText: '' }), fields: [{ kind: 'text', key: 'command', label: 'Command / program', placeholder: 'e.g. notepad.exe', mono: true }], }, { - type: P + 'CommandShortcut', label: 'Run shortcut', category: 'system', kinds: ['button'], icon: 'zap', + type: P + 'CommandShortcut', buildEmpty: () => ({ _type: P + 'CommandShortcut', shortcut: '', overlayText: '' }), fields: [{ kind: 'text', key: 'shortcut', label: 'Shortcut path', placeholder: '…/app.lnk', mono: true }], }, { - type: P + 'CommandEndProgram', label: 'End program', category: 'system', kinds: ['button'], icon: 'x', + type: P + 'CommandEndProgram', buildEmpty: () => ({ _type: P + 'CommandEndProgram', name: '', specific: false, overlayText: '' }), fields: [ { kind: 'text', key: 'name', label: 'Process name', placeholder: 'leave blank for focused app' }, @@ -156,12 +158,12 @@ export const COMMANDS: CommandDef[] = [ ], }, { - type: P + 'CommandKeystroke', label: 'Keystroke', category: 'system', kinds: ['button'], icon: 'keyboard', + type: P + 'CommandKeystroke', buildEmpty: () => ({ _type: P + 'CommandKeystroke', type: 'KEY', keystroke: '', text: '', overlayText: '' }), fields: [{ kind: 'keystroke' }], }, { - type: P + 'CommandMedia', label: 'Media', category: 'system', kinds: ['button'], icon: 'play', + type: P + 'CommandMedia', buildEmpty: () => ({ _type: P + 'CommandMedia', button: 'playPause', spotify: false, overlayText: '' }), fields: [ { @@ -176,7 +178,7 @@ export const COMMANDS: CommandDef[] = [ // Generic outputs: send to anything over HTTP/MQTT/OSC. On a dial the position maps (min/max or // formula) to the number replacing {{ value }}; on a button the value resolves at full scale. { - type: P + 'CommandHttpRequest', label: 'HTTP request', category: 'system', kinds: ['dial', 'button'], icon: 'zap', + type: P + 'CommandHttpRequest', buildEmpty: () => ({ _type: P + 'CommandHttpRequest', url: '', method: 'GET', headers: '', body: '', min: 0, max: 100, formula: '', dialParams: dialParams(), invert: false }), fields: [ { kind: 'text', key: 'url', label: 'URL', placeholder: 'https://host/path?v={{ value }}', mono: true }, @@ -194,7 +196,7 @@ export const COMMANDS: CommandDef[] = [ ], }, { - type: P + 'CommandMqttPublish', label: 'MQTT publish', category: 'system', kinds: ['dial', 'button'], icon: 'zap', + type: P + 'CommandMqttPublish', buildEmpty: () => ({ _type: P + 'CommandMqttPublish', topic: '', payload: '', min: 0, max: 100, formula: '', dialParams: dialParams(), invert: false }), fields: [ { kind: 'text', key: 'topic', label: 'Topic', placeholder: 'home/livingroom/light', mono: true }, @@ -205,7 +207,7 @@ export const COMMANDS: CommandDef[] = [ ], }, { - type: P + 'CommandOscSend', label: 'OSC send', category: 'system', kinds: ['dial', 'button'], icon: 'sliders', + type: P + 'CommandOscSend', buildEmpty: () => ({ _type: P + 'CommandOscSend', address: '', min: 0, max: 100, formula: '', dialParams: dialParams(), invert: false }), fields: [ { kind: 'text', key: 'address', label: 'OSC address', placeholder: '/track/1/volume', mono: true }, @@ -217,17 +219,17 @@ export const COMMANDS: CommandDef[] = [ // ── INTEGRATIONS ─────────────────────────────────────────────────────────── { - type: P + 'CommandObsSetSourceVolume', label: 'OBS — source volume', category: 'integration', integration: 'obs', kinds: ['dial'], icon: 'sliders', + type: P + 'CommandObsSetSourceVolume', buildEmpty: () => ({ _type: P + 'CommandObsSetSourceVolume', sourceName: '', dialParams: dialParams(), invert: false }), fields: [{ kind: 'select-live', key: 'sourceName', label: 'Source', source: 'obs-sources' }], }, { - type: P + 'CommandObsSetScene', label: 'OBS — switch scene', category: 'integration', integration: 'obs', kinds: ['button'], icon: 'film', + type: P + 'CommandObsSetScene', buildEmpty: () => ({ _type: P + 'CommandObsSetScene', scene: '', overlayText: '' }), fields: [{ kind: 'select-live', key: 'scene', label: 'Scene', source: 'obs-scenes' }], }, { - type: P + 'CommandObsMuteSource', label: 'OBS — mute source', category: 'integration', integration: 'obs', kinds: ['button'], icon: 'mic-off', + type: P + 'CommandObsMuteSource', buildEmpty: () => ({ _type: P + 'CommandObsMuteSource', source: '', type: 'toggle', overlayText: '' }), fields: [ { kind: 'select-live', key: 'source', label: 'Source', source: 'obs-sources' }, @@ -235,7 +237,7 @@ export const COMMANDS: CommandDef[] = [ ], }, { - type: P + 'CommandObsAction', label: 'OBS — stream / record', category: 'integration', integration: 'obs', kinds: ['button'], icon: 'film', + type: P + 'CommandObsAction', buildEmpty: () => ({ _type: P + 'CommandObsAction', action: 'TOGGLE_STREAM', overlayText: '' }), fields: [ { @@ -250,7 +252,7 @@ export const COMMANDS: CommandDef[] = [ ], }, { - type: P + 'CommandVoiceMeeterAdvanced', label: 'Voicemeeter — parameter', category: 'integration', integration: 'voicemeeter', kinds: ['dial'], icon: 'sliders', + type: P + 'CommandVoiceMeeterAdvanced', buildEmpty: () => ({ _type: P + 'CommandVoiceMeeterAdvanced', ct: 'NEG_12_TO_12', fullParam: '', dialParams: dialParams(), invert: false }), fields: [ { kind: 'select-live', key: 'fullParam', label: 'Parameter', source: 'vm-advanced' }, @@ -264,7 +266,7 @@ export const COMMANDS: CommandDef[] = [ ], }, { - type: P + 'CommandVoiceMeeterAdvancedButton', label: 'Voicemeeter — button', category: 'integration', integration: 'voicemeeter', kinds: ['button'], icon: 'sliders', + type: P + 'CommandVoiceMeeterAdvancedButton', buildEmpty: () => ({ _type: P + 'CommandVoiceMeeterAdvancedButton', bt: 'TOGGLE', fullParam: '', stringValue: '', overlayText: '' }), fields: [ { kind: 'select-live', key: 'fullParam', label: 'Parameter', source: 'vm-advanced' }, @@ -277,7 +279,7 @@ export const COMMANDS: CommandDef[] = [ ], }, { - type: WL + 'CommandWaveLinkChangeLevel', label: 'Wave Link — level', category: 'integration', integration: 'wavelink', kinds: ['dial'], icon: 'sliders', + type: WL + 'CommandWaveLinkChangeLevel', buildEmpty: () => ({ _type: WL + 'CommandWaveLinkChangeLevel', commandType: 'Channel', id1: '', id2: '', dialParams: dialParams(), invert: false }), fields: [ { @@ -290,7 +292,7 @@ export const COMMANDS: CommandDef[] = [ ], }, { - type: WL + 'CommandWaveLinkChangeMute', label: 'Wave Link — mute', category: 'integration', integration: 'wavelink', kinds: ['button'], icon: 'mic-off', + type: WL + 'CommandWaveLinkChangeMute', buildEmpty: () => ({ _type: WL + 'CommandWaveLinkChangeMute', commandType: 'Channel', id1: '', id2: '', muteType: 'toggle', overlayText: '' }), fields: [ { @@ -304,17 +306,17 @@ export const COMMANDS: CommandDef[] = [ ], }, { - type: WL + 'CommandWaveLinkMainOutput', label: 'Wave Link — main output', category: 'integration', integration: 'wavelink', kinds: ['button'], icon: 'volume', + type: WL + 'CommandWaveLinkMainOutput', buildEmpty: () => ({ _type: WL + 'CommandWaveLinkMainOutput', id: '', name: '', overlayText: '' }), fields: [{ kind: 'select-live', key: 'id', label: 'Output', source: 'wl-outputs' }], }, { - type: WL + 'CommandWaveLinkAddFocusToChannel', label: 'Wave Link — add focused app', category: 'integration', integration: 'wavelink', kinds: ['button'], icon: 'plus', + type: WL + 'CommandWaveLinkAddFocusToChannel', buildEmpty: () => ({ _type: WL + 'CommandWaveLinkAddFocusToChannel', id: '', name: '', overlayText: '' }), fields: [{ kind: 'select-live', key: 'id', label: 'Channel', source: 'wl-channels' }], }, { - type: DC + 'CommandDiscordMute', label: 'Discord — mute', category: 'integration', integration: 'discord', kinds: ['button'], icon: 'mic-off', + type: DC + 'CommandDiscordMute', buildEmpty: () => ({ _type: DC + 'CommandDiscordMute', target: 'self', muteType: 'toggle', overlayText: '' }), fields: [ { kind: 'select-live', key: 'target', label: 'Target', source: 'discord-mute-targets', searchable: true }, @@ -322,12 +324,12 @@ export const COMMANDS: CommandDef[] = [ ], }, { - type: DC + 'CommandDiscordSelfDeafen', label: 'Discord — deafen self', category: 'integration', integration: 'discord', kinds: ['button'], icon: 'volume-x', + type: DC + 'CommandDiscordSelfDeafen', buildEmpty: () => ({ _type: DC + 'CommandDiscordSelfDeafen', muteType: 'toggle', overlayText: '' }), fields: [{ kind: 'mute', key: 'muteType', label: 'Action' }], }, { - type: DC + 'CommandDiscordVolume', label: 'Discord — volume', category: 'integration', integration: 'discord', kinds: ['dial'], icon: 'volume', + type: DC + 'CommandDiscordVolume', buildEmpty: () => ({ _type: DC + 'CommandDiscordVolume', target: 'mic', clearMuteOnChange: false, dialParams: dialParams(), invert: false }), fields: [ { kind: 'select-live', key: 'target', label: 'Target', source: 'discord-volume-targets', searchable: true }, @@ -335,7 +337,7 @@ export const COMMANDS: CommandDef[] = [ ], }, { - type: DC + 'CommandDiscordScreenShare', label: 'Discord — screen share', category: 'integration', integration: 'discord', kinds: ['button'], icon: 'monitor', + type: DC + 'CommandDiscordScreenShare', buildEmpty: () => ({ _type: DC + 'CommandDiscordScreenShare', mode: 'SCREEN', processName: [], overlayText: '' }), fields: [ { @@ -347,22 +349,22 @@ export const COMMANDS: CommandDef[] = [ ], }, { - type: DC + 'CommandDiscordToggleVideo', label: 'Discord — toggle camera', category: 'integration', integration: 'discord', kinds: ['button'], icon: 'film', + type: DC + 'CommandDiscordToggleVideo', buildEmpty: () => ({ _type: DC + 'CommandDiscordToggleVideo', overlayText: '' }), fields: [], }, { - type: DC + 'CommandDiscordJoinVoice', label: 'Discord — join voice', category: 'integration', integration: 'discord', kinds: ['button'], icon: 'plug', + type: DC + 'CommandDiscordJoinVoice', buildEmpty: () => ({ _type: DC + 'CommandDiscordJoinVoice', channelId: '', channelName: '', overlayText: '' }), fields: [{ kind: 'select-live', key: 'channelId', label: 'Voice channel', source: 'discord-channels' }], }, { - type: DC + 'CommandDiscordLeaveVoice', label: 'Discord — leave voice', category: 'integration', integration: 'discord', kinds: ['button'], icon: 'log-out', + type: DC + 'CommandDiscordLeaveVoice', buildEmpty: () => ({ _type: DC + 'CommandDiscordLeaveVoice', overlayText: '' }), fields: [], }, { - type: HA + 'CommandHomeAssistantValue', label: 'Home Assistant — set value', category: 'integration', integration: 'homeassistant', kinds: ['dial'], icon: 'sliders', + type: HA + 'CommandHomeAssistantValue', buildEmpty: () => ({ _type: HA + 'CommandHomeAssistantValue', server: '', action: '', min: 0, max: 100, formula: '', dialParams: dialParams(), invert: false }), fields: [ { kind: 'select-live', key: 'server', label: 'Server', source: 'ha-servers' }, @@ -374,7 +376,7 @@ export const COMMANDS: CommandDef[] = [ ], }, { - type: HA + 'CommandHomeAssistantAction', label: 'Home Assistant — perform action', category: 'integration', integration: 'homeassistant', kinds: ['button'], icon: 'zap', + type: HA + 'CommandHomeAssistantAction', buildEmpty: () => ({ _type: HA + 'CommandHomeAssistantAction', server: '', action: '', overlayText: '' }), fields: [ { kind: 'select-live', key: 'server', label: 'Server', source: 'ha-servers' }, @@ -384,6 +386,17 @@ export const COMMANDS: CommandDef[] = [ }, ]; +// COMMANDS is assembled from the Java-generated registry (label/category/kinds/integration/icon) +// joined with the hand-written field editors above, keyed by the command's persisted type id. +const FIELDS_BY_TYPE = new Map(FIELD_DEFS.map(d => [d.type, d] as const)); +export const COMMANDS: CommandDef[] = GENERATED_COMMANDS.map(g => { + // field schemas are keyed by the command's historical id; join on `legacy` so renaming the + // persisted id to a nice one needs no change here. buildEmpty stamps the current (nice) _type. + const f = FIELDS_BY_TYPE.get(g.legacy ?? g.type); + if (!f) throw new Error('No field schema for command ' + g.type); + return { ...g, buildEmpty: () => ({ ...f.buildEmpty(), _type: g.type }), fields: f.fields }; +}); + export const COMMAND_BY_TYPE = new Map(COMMANDS.map(c => [c.type, c])); export function commandsForKind(kind: CommandKind): CommandDef[] { diff --git a/src/main/webui/src/app/features/commands/command-registry.generated.ts b/src/main/webui/src/app/features/commands/command-registry.generated.ts new file mode 100644 index 00000000..b4a86664 --- /dev/null +++ b/src/main/webui/src/app/features/commands/command-registry.generated.ts @@ -0,0 +1,57 @@ +// GENERATED FILE — do not edit. Source: @CommandMeta on the command classes +// (com.getpcpanel.**.command). Regenerate via CommandRegistryGeneratorTest. +import { IconName } from '../../ui'; +import type { CommandCategory, CommandKind, Integration } from './command-catalog'; + +export interface GeneratedCommand { + type: string; + label: string; + category: CommandCategory; + kinds: CommandKind[]; + integration?: Integration; + icon: IconName; + /** previous _type id(s) for joining hand-written field schemas keyed by the old id */ + legacy?: string; +} + +export const GENERATED_COMMANDS: GeneratedCommand[] = [ + { type: 'analogbands.ranges', label: 'Stepped switch (ranges)', category: 'system', kinds: ['dial'], icon: 'sliders', legacy: 'com.getpcpanel.commands.command.CommandAnalogBands' }, + { type: 'device.brightness', label: 'Brightness', category: 'system', kinds: ['dial'], icon: 'sun', legacy: 'com.getpcpanel.commands.command.CommandBrightness' }, + { type: 'discord.join-voice', label: 'Discord — join voice', category: 'integration', kinds: ['button'], integration: 'discord', icon: 'plug', legacy: 'com.getpcpanel.discord.command.CommandDiscordJoinVoice' }, + { type: 'discord.leave-voice', label: 'Discord — leave voice', category: 'integration', kinds: ['button'], integration: 'discord', icon: 'log-out', legacy: 'com.getpcpanel.discord.command.CommandDiscordLeaveVoice' }, + { type: 'discord.mute', label: 'Discord — mute', category: 'integration', kinds: ['button'], integration: 'discord', icon: 'mic-off', legacy: 'com.getpcpanel.discord.command.CommandDiscordMute' }, + { type: 'discord.screen-share', label: 'Discord — screen share', category: 'integration', kinds: ['button'], integration: 'discord', icon: 'monitor', legacy: 'com.getpcpanel.discord.command.CommandDiscordScreenShare' }, + { type: 'discord.self-deafen', label: 'Discord — deafen self', category: 'integration', kinds: ['button'], integration: 'discord', icon: 'volume-x', legacy: 'com.getpcpanel.discord.command.CommandDiscordSelfDeafen' }, + { type: 'discord.toggle-video', label: 'Discord — toggle camera', category: 'integration', kinds: ['button'], integration: 'discord', icon: 'film', legacy: 'com.getpcpanel.discord.command.CommandDiscordToggleVideo' }, + { type: 'discord.volume', label: 'Discord — volume', category: 'integration', kinds: ['dial'], integration: 'discord', icon: 'volume', legacy: 'com.getpcpanel.discord.command.CommandDiscordVolume' }, + { type: 'homeassistant.action', label: 'Home Assistant — perform action', category: 'integration', kinds: ['button'], integration: 'homeassistant', icon: 'zap', legacy: 'com.getpcpanel.homeassistant.command.CommandHomeAssistantAction' }, + { type: 'homeassistant.value', label: 'Home Assistant — set value', category: 'integration', kinds: ['dial'], integration: 'homeassistant', icon: 'sliders', legacy: 'com.getpcpanel.homeassistant.command.CommandHomeAssistantValue' }, + { type: 'keyboard.keystroke', label: 'Keystroke', category: 'system', kinds: ['button'], icon: 'keyboard', legacy: 'com.getpcpanel.commands.command.CommandKeystroke' }, + { type: 'keyboard.media', label: 'Media', category: 'system', kinds: ['button'], icon: 'play', legacy: 'com.getpcpanel.commands.command.CommandMedia' }, + { type: 'mqtt.publish', label: 'MQTT publish', category: 'system', kinds: ['dial', 'button'], icon: 'zap', legacy: 'com.getpcpanel.commands.command.CommandMqttPublish' }, + { type: 'obs.action', label: 'OBS — stream / record', category: 'integration', kinds: ['button'], integration: 'obs', icon: 'film', legacy: 'com.getpcpanel.commands.command.CommandObsAction' }, + { type: 'obs.mute-source', label: 'OBS — mute source', category: 'integration', kinds: ['button'], integration: 'obs', icon: 'mic-off', legacy: 'com.getpcpanel.commands.command.CommandObsMuteSource' }, + { type: 'obs.set-scene', label: 'OBS — switch scene', category: 'integration', kinds: ['button'], integration: 'obs', icon: 'film', legacy: 'com.getpcpanel.commands.command.CommandObsSetScene' }, + { type: 'obs.set-source-volume', label: 'OBS — source volume', category: 'integration', kinds: ['dial'], integration: 'obs', icon: 'sliders', legacy: 'com.getpcpanel.commands.command.CommandObsSetSourceVolume' }, + { type: 'osc.send', label: 'OSC send', category: 'system', kinds: ['dial', 'button'], icon: 'sliders', legacy: 'com.getpcpanel.commands.command.CommandOscSend' }, + { type: 'output.http-request', label: 'HTTP request', category: 'system', kinds: ['dial', 'button'], icon: 'zap', legacy: 'com.getpcpanel.commands.command.CommandHttpRequest' }, + { type: 'profile.switch', label: 'Switch profile', category: 'system', kinds: ['button'], icon: 'refresh', legacy: 'com.getpcpanel.commands.command.CommandProfile' }, + { type: 'program.end-program', label: 'End program', category: 'system', kinds: ['button'], icon: 'x', legacy: 'com.getpcpanel.commands.command.CommandEndProgram' }, + { type: 'program.run', label: 'Run command', category: 'system', kinds: ['button'], icon: 'zap', legacy: 'com.getpcpanel.commands.command.CommandRun' }, + { type: 'program.shortcut', label: 'Run shortcut', category: 'system', kinds: ['button'], icon: 'zap', legacy: 'com.getpcpanel.commands.command.CommandShortcut' }, + { type: 'voicemeeter.advanced', label: 'Voicemeeter — parameter', category: 'integration', kinds: ['dial'], integration: 'voicemeeter', icon: 'sliders', legacy: 'com.getpcpanel.commands.command.CommandVoiceMeeterAdvanced' }, + { type: 'voicemeeter.advanced-button', label: 'Voicemeeter — button', category: 'integration', kinds: ['button'], integration: 'voicemeeter', icon: 'sliders', legacy: 'com.getpcpanel.commands.command.CommandVoiceMeeterAdvancedButton' }, + { type: 'volume.default-device', label: 'Set default device', category: 'audio', kinds: ['button'], icon: 'monitor', legacy: 'com.getpcpanel.commands.command.CommandVolumeDefaultDevice' }, + { type: 'volume.default-device-advanced', label: 'Advanced default device', category: 'audio', kinds: ['button'], icon: 'monitor', legacy: 'com.getpcpanel.commands.command.CommandVolumeDefaultDeviceAdvanced' }, + { type: 'volume.default-device-toggle', label: 'Cycle default device', category: 'audio', kinds: ['button'], icon: 'refresh', legacy: 'com.getpcpanel.commands.command.CommandVolumeDefaultDeviceToggle' }, + { type: 'volume.device', label: 'Device volume', category: 'audio', kinds: ['dial'], icon: 'volume', legacy: 'com.getpcpanel.commands.command.CommandVolumeDevice' }, + { type: 'volume.device-mute', label: 'Device mute', category: 'audio', kinds: ['button'], icon: 'volume-x', legacy: 'com.getpcpanel.commands.command.CommandVolumeDeviceMute' }, + { type: 'volume.focus', label: 'Focused-app volume', category: 'audio', kinds: ['dial'], icon: 'volume', legacy: 'com.getpcpanel.commands.command.CommandVolumeFocus' }, + { type: 'volume.focus-mute', label: 'Focused-app mute', category: 'audio', kinds: ['button'], icon: 'volume-x', legacy: 'com.getpcpanel.commands.command.CommandVolumeFocusMute' }, + { type: 'volume.process', label: 'App volume', category: 'audio', kinds: ['dial'], icon: 'volume', legacy: 'com.getpcpanel.commands.command.CommandVolumeProcess' }, + { type: 'volume.process-mute', label: 'App mute', category: 'audio', kinds: ['button'], icon: 'volume-x', legacy: 'com.getpcpanel.commands.command.CommandVolumeProcessMute' }, + { type: 'wavelink.add-focus-to-channel', label: 'Wave Link — add focused app', category: 'integration', kinds: ['button'], integration: 'wavelink', icon: 'plus', legacy: 'com.getpcpanel.wavelink.command.CommandWaveLinkAddFocusToChannel' }, + { type: 'wavelink.change-level', label: 'Wave Link — level', category: 'integration', kinds: ['dial'], integration: 'wavelink', icon: 'sliders', legacy: 'com.getpcpanel.wavelink.command.CommandWaveLinkChangeLevel' }, + { type: 'wavelink.change-mute', label: 'Wave Link — mute', category: 'integration', kinds: ['button'], integration: 'wavelink', icon: 'mic-off', legacy: 'com.getpcpanel.wavelink.command.CommandWaveLinkChangeMute' }, + { type: 'wavelink.main-output', label: 'Wave Link — main output', category: 'integration', kinds: ['button'], integration: 'wavelink', icon: 'volume', legacy: 'com.getpcpanel.wavelink.command.CommandWaveLinkMainOutput' }, +]; diff --git a/src/main/webui/src/app/models/generated/backend.types.ts b/src/main/webui/src/app/models/generated/backend.types.ts index 67d6cbde..44ceee61 100644 --- a/src/main/webui/src/app/models/generated/backend.types.ts +++ b/src/main/webui/src/app/models/generated/backend.types.ts @@ -15,6 +15,9 @@ export interface AnalogBand { start: number; } +export interface AnalogBandsCommandModule extends CommandModule { +} + export interface AnalogInputSpec { hasButton: boolean; id: string; @@ -34,54 +37,60 @@ export interface AnalogOutputSpec { min: number; } +export interface BandTransition { + band: number; + changed: boolean; + fire: boolean; +} + export interface ButtonAction { overlayText?: string; } export interface Command { - _type: "com.getpcpanel.commands.command.CommandAnalogBands" | "com.getpcpanel.commands.command.CommandBrightness" | "com.getpcpanel.commands.command.CommandEndProgram" | "com.getpcpanel.commands.command.CommandKeystroke" | "com.getpcpanel.commands.command.CommandMedia" | "com.getpcpanel.commands.command.CommandNoOp" | "com.getpcpanel.commands.command.CommandObs" | "com.getpcpanel.commands.command.CommandObsAction" | "com.getpcpanel.commands.command.CommandObsMuteSource" | "com.getpcpanel.commands.command.CommandObsSetScene" | "com.getpcpanel.commands.command.CommandObsSetSourceVolume" | "com.getpcpanel.commands.command.CommandProfile" | "com.getpcpanel.commands.command.CommandRun" | "com.getpcpanel.commands.command.CommandShortcut" | "com.getpcpanel.commands.command.CommandValueOutput" | "com.getpcpanel.commands.command.CommandHttpRequest" | "com.getpcpanel.commands.command.CommandMqttPublish" | "com.getpcpanel.commands.command.CommandOscSend" | "com.getpcpanel.commands.command.CommandVoiceMeeter" | "com.getpcpanel.commands.command.CommandVoiceMeeterAdvanced" | "com.getpcpanel.commands.command.CommandVoiceMeeterAdvancedButton" | "com.getpcpanel.commands.command.CommandVoiceMeeterBasic" | "com.getpcpanel.commands.command.CommandVoiceMeeterBasicButton" | "com.getpcpanel.commands.command.CommandVolume" | "com.getpcpanel.commands.command.CommandVolumeApplicationDeviceToggle" | "com.getpcpanel.commands.command.CommandVolumeDefaultDevice" | "com.getpcpanel.commands.command.CommandVolumeDefaultDeviceAdvanced" | "com.getpcpanel.commands.command.CommandVolumeDefaultDeviceToggle" | "com.getpcpanel.commands.command.CommandVolumeDefaultDeviceToggleAdvanced" | "com.getpcpanel.commands.command.CommandVolumeDevice" | "com.getpcpanel.commands.command.CommandVolumeDeviceMute" | "com.getpcpanel.commands.command.CommandVolumeFocus" | "com.getpcpanel.commands.command.CommandVolumeFocusMute" | "com.getpcpanel.commands.command.CommandVolumeProcess" | "com.getpcpanel.commands.command.CommandVolumeProcessMute" | "com.getpcpanel.discord.command.CommandDiscord" | "com.getpcpanel.discord.command.CommandDiscordJoinVoice" | "com.getpcpanel.discord.command.CommandDiscordLeaveVoice" | "com.getpcpanel.discord.command.CommandDiscordMute" | "com.getpcpanel.discord.command.CommandDiscordScreenShare" | "com.getpcpanel.discord.command.CommandDiscordSelfDeafen" | "com.getpcpanel.discord.command.CommandDiscordSelfInputVolume" | "com.getpcpanel.discord.command.CommandDiscordSelfMute" | "com.getpcpanel.discord.command.CommandDiscordSelfOutputVolume" | "com.getpcpanel.discord.command.CommandDiscordToggleVideo" | "com.getpcpanel.discord.command.CommandDiscordUserMute" | "com.getpcpanel.discord.command.CommandDiscordUserVolume" | "com.getpcpanel.discord.command.CommandDiscordVolume" | "com.getpcpanel.homeassistant.command.CommandHomeAssistant" | "com.getpcpanel.homeassistant.command.CommandHomeAssistantAction" | "com.getpcpanel.homeassistant.command.CommandHomeAssistantValue" | "com.getpcpanel.wavelink.command.CommandWaveLink" | "com.getpcpanel.wavelink.command.CommandWaveLinkAddFocusToChannel" | "com.getpcpanel.wavelink.command.CommandWaveLinkChange" | "com.getpcpanel.wavelink.command.CommandWaveLinkChangeLevel" | "com.getpcpanel.wavelink.command.CommandWaveLinkChangeMute" | "com.getpcpanel.wavelink.command.CommandWaveLinkChannelEffect" | "com.getpcpanel.wavelink.command.CommandWaveLinkMainOutput"; + _type: "com.getpcpanel.commands.command.CommandNoOp" | "mqtt.publish" | "osc.send" | "output.http-request" | "analogbands.ranges" | "device.brightness" | "discord.join-voice" | "discord.leave-voice" | "discord.mute" | "discord.screen-share" | "discord.self-deafen" | "com.getpcpanel.discord.command.CommandDiscordSelfInputVolume" | "com.getpcpanel.discord.command.CommandDiscordSelfMute" | "com.getpcpanel.discord.command.CommandDiscordSelfOutputVolume" | "discord.toggle-video" | "com.getpcpanel.discord.command.CommandDiscordUserMute" | "com.getpcpanel.discord.command.CommandDiscordUserVolume" | "discord.volume" | "homeassistant.action" | "homeassistant.value" | "keyboard.keystroke" | "keyboard.media" | "obs.action" | "obs.mute-source" | "obs.set-scene" | "obs.set-source-volume" | "profile.switch" | "program.end-program" | "program.run" | "program.shortcut" | "voicemeeter.advanced" | "voicemeeter.advanced-button" | "com.getpcpanel.commands.command.CommandVoiceMeeterBasic" | "com.getpcpanel.commands.command.CommandVoiceMeeterBasicButton" | "com.getpcpanel.commands.command.CommandVolumeApplicationDeviceToggle" | "volume.default-device" | "volume.default-device-advanced" | "volume.default-device-toggle" | "com.getpcpanel.commands.command.CommandVolumeDefaultDeviceToggleAdvanced" | "volume.device" | "volume.device-mute" | "volume.focus" | "volume.focus-mute" | "volume.process" | "volume.process-mute" | "wavelink.add-focus-to-channel" | "wavelink.change-level" | "wavelink.change-mute" | "com.getpcpanel.wavelink.command.CommandWaveLinkChannelEffect" | "wavelink.main-output"; } export interface CommandAnalogBands extends Command, DialAction { - _type: "com.getpcpanel.commands.command.CommandAnalogBands"; + _type: "analogbands.ranges"; bands?: AnalogBand[]; } export interface CommandBrightness extends Command, DialAction { - _type: "com.getpcpanel.commands.command.CommandBrightness"; + _type: "device.brightness"; } export interface CommandConverter { } export interface CommandDiscord extends Command { - _type: "com.getpcpanel.discord.command.CommandDiscord" | "com.getpcpanel.discord.command.CommandDiscordJoinVoice" | "com.getpcpanel.discord.command.CommandDiscordLeaveVoice" | "com.getpcpanel.discord.command.CommandDiscordMute" | "com.getpcpanel.discord.command.CommandDiscordScreenShare" | "com.getpcpanel.discord.command.CommandDiscordSelfDeafen" | "com.getpcpanel.discord.command.CommandDiscordSelfInputVolume" | "com.getpcpanel.discord.command.CommandDiscordSelfMute" | "com.getpcpanel.discord.command.CommandDiscordSelfOutputVolume" | "com.getpcpanel.discord.command.CommandDiscordToggleVideo" | "com.getpcpanel.discord.command.CommandDiscordUserMute" | "com.getpcpanel.discord.command.CommandDiscordUserVolume" | "com.getpcpanel.discord.command.CommandDiscordVolume"; + _type: "discord.join-voice" | "discord.leave-voice" | "discord.mute" | "discord.screen-share" | "discord.self-deafen" | "com.getpcpanel.discord.command.CommandDiscordSelfInputVolume" | "com.getpcpanel.discord.command.CommandDiscordSelfMute" | "com.getpcpanel.discord.command.CommandDiscordSelfOutputVolume" | "discord.toggle-video" | "com.getpcpanel.discord.command.CommandDiscordUserMute" | "com.getpcpanel.discord.command.CommandDiscordUserVolume" | "discord.volume"; } export interface CommandDiscordJoinVoice extends CommandDiscord, ButtonAction { - _type: "com.getpcpanel.discord.command.CommandDiscordJoinVoice"; + _type: "discord.join-voice"; channelId?: string; channelName?: string; } export interface CommandDiscordLeaveVoice extends CommandDiscord, ButtonAction { - _type: "com.getpcpanel.discord.command.CommandDiscordLeaveVoice"; + _type: "discord.leave-voice"; } export interface CommandDiscordMute extends CommandDiscord, ButtonAction { - _type: "com.getpcpanel.discord.command.CommandDiscordMute"; + _type: "discord.mute"; muteType?: MuteType; target?: string; } export interface CommandDiscordScreenShare extends CommandDiscord, ButtonAction { - _type: "com.getpcpanel.discord.command.CommandDiscordScreenShare"; + _type: "discord.screen-share"; mode?: Mode; processName?: string[]; } export interface CommandDiscordSelfDeafen extends CommandDiscord, ButtonAction { - _type: "com.getpcpanel.discord.command.CommandDiscordSelfDeafen"; + _type: "discord.self-deafen"; muteType?: MuteType; } @@ -101,7 +110,7 @@ export interface CommandDiscordSelfOutputVolume extends CommandDiscord, DialActi } export interface CommandDiscordToggleVideo extends CommandDiscord, ButtonAction { - _type: "com.getpcpanel.discord.command.CommandDiscordToggleVideo"; + _type: "discord.toggle-video"; } export interface CommandDiscordUserMute extends CommandDiscord, ButtonAction { @@ -116,29 +125,29 @@ export interface CommandDiscordUserVolume extends CommandDiscord, DialAction { } export interface CommandDiscordVolume extends CommandDiscord, DialAction { - _type: "com.getpcpanel.discord.command.CommandDiscordVolume"; + _type: "discord.volume"; clearMuteOnChange: boolean; target?: string; } export interface CommandEndProgram extends Command, ButtonAction { - _type: "com.getpcpanel.commands.command.CommandEndProgram"; + _type: "program.end-program"; name: string; specific: boolean; } export interface CommandHomeAssistant extends Command { - _type: "com.getpcpanel.homeassistant.command.CommandHomeAssistant" | "com.getpcpanel.homeassistant.command.CommandHomeAssistantAction" | "com.getpcpanel.homeassistant.command.CommandHomeAssistantValue"; + _type: "homeassistant.action" | "homeassistant.value"; server?: string; } export interface CommandHomeAssistantAction extends CommandHomeAssistant, ButtonAction { - _type: "com.getpcpanel.homeassistant.command.CommandHomeAssistantAction"; + _type: "homeassistant.action"; action: string; } export interface CommandHomeAssistantValue extends CommandHomeAssistant, DialAction { - _type: "com.getpcpanel.homeassistant.command.CommandHomeAssistantValue"; + _type: "homeassistant.value"; action: string; formula?: string; max?: number; @@ -146,7 +155,7 @@ export interface CommandHomeAssistantValue extends CommandHomeAssistant, DialAct } export interface CommandHttpRequest extends CommandValueOutput { - _type: "com.getpcpanel.commands.command.CommandHttpRequest"; + _type: "output.http-request"; body?: string; headers?: string; method: string; @@ -154,20 +163,23 @@ export interface CommandHttpRequest extends CommandValueOutput { } export interface CommandKeystroke extends Command, ButtonAction { - _type: "com.getpcpanel.commands.command.CommandKeystroke"; + _type: "keyboard.keystroke"; keystroke: string; text: string; type: KeystrokeType; } export interface CommandMedia extends Command, ButtonAction { - _type: "com.getpcpanel.commands.command.CommandMedia"; + _type: "keyboard.media"; button: VolumeButton; spotify: boolean; } +export interface CommandModule { +} + export interface CommandMqttPublish extends CommandValueOutput { - _type: "com.getpcpanel.commands.command.CommandMqttPublish"; + _type: "mqtt.publish"; payload: string; topic: string; } @@ -177,42 +189,42 @@ export interface CommandNoOp extends Command, ButtonAction, DialAction { } export interface CommandObs extends Command { - _type: "com.getpcpanel.commands.command.CommandObs" | "com.getpcpanel.commands.command.CommandObsAction" | "com.getpcpanel.commands.command.CommandObsMuteSource" | "com.getpcpanel.commands.command.CommandObsSetScene" | "com.getpcpanel.commands.command.CommandObsSetSourceVolume"; + _type: "obs.action" | "obs.mute-source" | "obs.set-scene" | "obs.set-source-volume"; } export interface CommandObsAction extends CommandObs, ButtonAction { - _type: "com.getpcpanel.commands.command.CommandObsAction"; + _type: "obs.action"; action: ObsActionType; } export interface CommandObsMuteSource extends CommandObs, ButtonAction { - _type: "com.getpcpanel.commands.command.CommandObsMuteSource"; + _type: "obs.mute-source"; source: string; type: MuteType; } export interface CommandObsSetScene extends CommandObs, ButtonAction { - _type: "com.getpcpanel.commands.command.CommandObsSetScene"; + _type: "obs.set-scene"; scene: string; } export interface CommandObsSetSourceVolume extends CommandObs, DialAction { - _type: "com.getpcpanel.commands.command.CommandObsSetSourceVolume"; + _type: "obs.set-source-volume"; sourceName: string; } export interface CommandOscSend extends CommandValueOutput { - _type: "com.getpcpanel.commands.command.CommandOscSend"; + _type: "osc.send"; address: string; } export interface CommandProfile extends Command, DeviceAction { - _type: "com.getpcpanel.commands.command.CommandProfile"; + _type: "profile.switch"; profile?: string; } export interface CommandRun extends Command, ButtonAction { - _type: "com.getpcpanel.commands.command.CommandRun"; + _type: "program.run"; command: string; } @@ -222,36 +234,29 @@ export interface Commands { } export interface CommandShortcut extends Command, ButtonAction { - _type: "com.getpcpanel.commands.command.CommandShortcut"; + _type: "program.shortcut"; shortcut: string; } -export interface CommandType { - category: CommandCategory; - command: string; - kind: Kinds; - name: string; -} - export interface CommandValueOutput extends Command, DialAction, ButtonAction { - _type: "com.getpcpanel.commands.command.CommandValueOutput" | "com.getpcpanel.commands.command.CommandHttpRequest" | "com.getpcpanel.commands.command.CommandMqttPublish" | "com.getpcpanel.commands.command.CommandOscSend"; + _type: "mqtt.publish" | "osc.send" | "output.http-request"; formula?: string; max?: number; min?: number; } export interface CommandVoiceMeeter extends Command { - _type: "com.getpcpanel.commands.command.CommandVoiceMeeter" | "com.getpcpanel.commands.command.CommandVoiceMeeterAdvanced" | "com.getpcpanel.commands.command.CommandVoiceMeeterAdvancedButton" | "com.getpcpanel.commands.command.CommandVoiceMeeterBasic" | "com.getpcpanel.commands.command.CommandVoiceMeeterBasicButton"; + _type: "voicemeeter.advanced" | "voicemeeter.advanced-button" | "com.getpcpanel.commands.command.CommandVoiceMeeterBasic" | "com.getpcpanel.commands.command.CommandVoiceMeeterBasicButton"; } export interface CommandVoiceMeeterAdvanced extends CommandVoiceMeeter, DialAction { - _type: "com.getpcpanel.commands.command.CommandVoiceMeeterAdvanced"; + _type: "voicemeeter.advanced"; ct: DialControlMode; fullParam: string; } export interface CommandVoiceMeeterAdvancedButton extends CommandVoiceMeeter, ButtonAction { - _type: "com.getpcpanel.commands.command.CommandVoiceMeeterAdvancedButton"; + _type: "voicemeeter.advanced-button"; bt: ButtonControlMode; fullParam: string; stringValue?: string; @@ -272,7 +277,7 @@ export interface CommandVoiceMeeterBasicButton extends CommandVoiceMeeter, Butto } export interface CommandVolume extends Command { - _type: "com.getpcpanel.commands.command.CommandVolume" | "com.getpcpanel.commands.command.CommandVolumeApplicationDeviceToggle" | "com.getpcpanel.commands.command.CommandVolumeDefaultDevice" | "com.getpcpanel.commands.command.CommandVolumeDefaultDeviceAdvanced" | "com.getpcpanel.commands.command.CommandVolumeDefaultDeviceToggle" | "com.getpcpanel.commands.command.CommandVolumeDefaultDeviceToggleAdvanced" | "com.getpcpanel.commands.command.CommandVolumeDevice" | "com.getpcpanel.commands.command.CommandVolumeDeviceMute" | "com.getpcpanel.commands.command.CommandVolumeFocus" | "com.getpcpanel.commands.command.CommandVolumeFocusMute" | "com.getpcpanel.commands.command.CommandVolumeProcess" | "com.getpcpanel.commands.command.CommandVolumeProcessMute"; + _type: "com.getpcpanel.commands.command.CommandVolumeApplicationDeviceToggle" | "volume.default-device" | "volume.default-device-advanced" | "volume.default-device-toggle" | "com.getpcpanel.commands.command.CommandVolumeDefaultDeviceToggleAdvanced" | "volume.device" | "volume.device-mute" | "volume.focus" | "volume.focus-mute" | "volume.process" | "volume.process-mute"; } export interface CommandVolumeApplicationDeviceToggle extends CommandVolume, ButtonAction { @@ -284,12 +289,12 @@ export interface CommandVolumeApplicationDeviceToggle extends CommandVolume, But } export interface CommandVolumeDefaultDevice extends CommandVolume, ButtonAction { - _type: "com.getpcpanel.commands.command.CommandVolumeDefaultDevice"; + _type: "volume.default-device"; deviceId: string; } export interface CommandVolumeDefaultDeviceAdvanced extends CommandVolume, ButtonAction { - _type: "com.getpcpanel.commands.command.CommandVolumeDefaultDeviceAdvanced"; + _type: "volume.default-device-advanced"; communicationPb: string; communicationRec: string; mediaPb: string; @@ -301,7 +306,7 @@ export interface CommandVolumeDefaultDeviceAdvancedBuilder { } export interface CommandVolumeDefaultDeviceToggle extends CommandVolume, ButtonAction { - _type: "com.getpcpanel.commands.command.CommandVolumeDefaultDeviceToggle"; + _type: "volume.default-device-toggle"; currentIdx: number; devices: string[]; } @@ -313,29 +318,29 @@ export interface CommandVolumeDefaultDeviceToggleAdvanced extends CommandVolume, } export interface CommandVolumeDevice extends CommandVolume, DialAction { - _type: "com.getpcpanel.commands.command.CommandVolumeDevice"; + _type: "volume.device"; deviceId: string; isUnMuteOnVolumeChange: boolean; unMuteOnVolumeChange: boolean; } export interface CommandVolumeDeviceMute extends CommandVolume, ButtonAction { - _type: "com.getpcpanel.commands.command.CommandVolumeDeviceMute"; + _type: "volume.device-mute"; deviceId: string; muteType: MuteType; } export interface CommandVolumeFocus extends CommandVolume, DialAction { - _type: "com.getpcpanel.commands.command.CommandVolumeFocus"; + _type: "volume.focus"; } export interface CommandVolumeFocusMute extends CommandVolume, ButtonAction { - _type: "com.getpcpanel.commands.command.CommandVolumeFocusMute"; + _type: "volume.focus-mute"; muteType: MuteType; } export interface CommandVolumeProcess extends CommandVolume, DialAction { - _type: "com.getpcpanel.commands.command.CommandVolumeProcess"; + _type: "volume.process"; device: string; isUnMuteOnVolumeChange: boolean; processName: string[]; @@ -343,34 +348,34 @@ export interface CommandVolumeProcess extends CommandVolume, DialAction { } export interface CommandVolumeProcessMute extends CommandVolume, ButtonAction { - _type: "com.getpcpanel.commands.command.CommandVolumeProcessMute"; + _type: "volume.process-mute"; muteType: MuteType; processName: string[]; } export interface CommandWaveLink extends Command { - _type: "com.getpcpanel.wavelink.command.CommandWaveLink" | "com.getpcpanel.wavelink.command.CommandWaveLinkAddFocusToChannel" | "com.getpcpanel.wavelink.command.CommandWaveLinkChange" | "com.getpcpanel.wavelink.command.CommandWaveLinkChangeLevel" | "com.getpcpanel.wavelink.command.CommandWaveLinkChangeMute" | "com.getpcpanel.wavelink.command.CommandWaveLinkChannelEffect" | "com.getpcpanel.wavelink.command.CommandWaveLinkMainOutput"; + _type: "wavelink.add-focus-to-channel" | "wavelink.change-level" | "wavelink.change-mute" | "com.getpcpanel.wavelink.command.CommandWaveLinkChannelEffect" | "wavelink.main-output"; } export interface CommandWaveLinkAddFocusToChannel extends CommandWaveLink, ButtonAction { - _type: "com.getpcpanel.wavelink.command.CommandWaveLinkAddFocusToChannel"; + _type: "wavelink.add-focus-to-channel"; id?: string; name?: string; } export interface CommandWaveLinkChange extends CommandWaveLink { - _type: "com.getpcpanel.wavelink.command.CommandWaveLinkChange" | "com.getpcpanel.wavelink.command.CommandWaveLinkChangeLevel" | "com.getpcpanel.wavelink.command.CommandWaveLinkChangeMute"; + _type: "wavelink.change-level" | "wavelink.change-mute"; commandType: WaveLinkCommandTarget; id1?: string; id2?: string; } export interface CommandWaveLinkChangeLevel extends CommandWaveLinkChange, DialAction { - _type: "com.getpcpanel.wavelink.command.CommandWaveLinkChangeLevel"; + _type: "wavelink.change-level"; } export interface CommandWaveLinkChangeMute extends CommandWaveLinkChange, ButtonAction { - _type: "com.getpcpanel.wavelink.command.CommandWaveLinkChangeMute"; + _type: "wavelink.change-mute"; muteType: MuteType; } @@ -384,7 +389,7 @@ export interface CommandWaveLinkChannelEffect extends CommandWaveLink, ButtonAct } export interface CommandWaveLinkMainOutput extends CommandWaveLink, ButtonAction { - _type: "com.getpcpanel.wavelink.command.CommandWaveLinkMainOutput"; + _type: "wavelink.main-output"; id?: string; name?: string; } @@ -404,6 +409,9 @@ export interface DeviceActionParameters { device: string; } +export interface DeviceCommandModule extends CommandModule { +} + export interface DeviceDescriptor { analogInputs: AnalogInputSpec[]; analogOutputs: AnalogOutputSpec[]; @@ -498,6 +506,9 @@ export interface DiscordAuth { userName?: string; } +export interface DiscordCommandModule extends CommandModule { +} + export interface DiscordSeenUser { displayName: string; id: string; @@ -534,6 +545,9 @@ export interface DiscordVoiceChannelDto { name: string; } +export interface EngineCommandModule extends CommandModule { +} + export interface FocusVolumeOverride { includeSource: boolean; sources: string[]; @@ -552,6 +566,9 @@ export interface GlobalLightingSpec { supportedModes: string[]; } +export interface HomeAssistantCommandModule extends CommandModule { +} + export interface HomeAssistantServer { id: string; name: string; @@ -572,6 +589,9 @@ export interface HomeAssistantSettings { enableDiscovery: boolean; } +export interface KeyboardCommandModule extends CommandModule { +} + export interface KnobSetting { buttonDebounce: number; logarithmic: boolean; @@ -623,6 +643,9 @@ export interface MidiDeviceDto { name: string; } +export interface MqttCommandModule extends CommandModule { +} + export interface MqttSettings { baseTopic: string; enabled: boolean; @@ -634,6 +657,9 @@ export interface MqttSettings { username: string; } +export interface ObsCommandModule extends CommandModule { +} + export interface OnboardingDto { changelogUrl: string; intent: string; @@ -647,11 +673,17 @@ export interface OSCBinding { toggle: boolean; } +export interface OscCommandModule extends CommandModule { +} + export interface OSCConnectionInfo { host: string; port: number; } +export interface OutputCommandModule extends CommandModule { +} + export interface ProcessDto { icon?: string; name: string; @@ -659,6 +691,9 @@ export interface ProcessDto { pid: number; } +export interface ProfileCommandModule extends CommandModule { +} + export interface ProfileDto { isMainProfile: boolean; name: string; @@ -681,6 +716,9 @@ export interface ProfileSnapshotDto { releaseButtonData: { [index: string]: Commands }; } +export interface ProgramCommandModule extends CommandModule { +} + export interface SerialPortDto { description: string; port: string; @@ -766,6 +804,12 @@ export interface SingleSliderLightingConfig { muteOverrideDeviceOrFollow: string; } +export interface VoiceMeeterCommandModule extends CommandModule { +} + +export interface VolumeCommandModule extends CommandModule { +} + export interface WaveLinkAppDto { id: string; name: string; @@ -781,6 +825,9 @@ export interface WaveLinkChannelDto { type?: string; } +export interface WaveLinkCommandModule extends CommandModule { +} + export interface WaveLinkEffectDto { id: string; isEnabled: boolean; @@ -911,8 +958,6 @@ export type ButtonControlMode = "ENABLE" | "DISABLE" | "TOGGLE" | "STRING"; export type ButtonType = "MONO" | "MUTE" | "SOLO" | "MC" | "EQ" | "A1" | "A2" | "A3" | "A4" | "A5" | "B1" | "B2" | "B3" | "SEL" | "MIXA" | "MIXB" | "REPEAT" | "COMPOSITE"; -export type CommandCategory = "standard" | "voicemeeter" | "obs" | "wavelink"; - export type CommandsType = "allAtOnce" | "sequential"; export type ControlType = "STRIP" | "BUS"; diff --git a/src/mcp/java/com/getpcpanel/mcp/AudioTools.java b/src/mcp/java/com/getpcpanel/mcp/AudioTools.java index 1738b801..5b826544 100644 --- a/src/mcp/java/com/getpcpanel/mcp/AudioTools.java +++ b/src/mcp/java/com/getpcpanel/mcp/AudioTools.java @@ -8,8 +8,8 @@ import org.apache.commons.lang3.StringUtils; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.volume.VolumeCoordinatorService; +import com.getpcpanel.integration.volume.platform.ISndCtrl; +import com.getpcpanel.integration.volume.VolumeCoordinatorService; import io.quarkus.arc.properties.IfBuildProperty; import io.quarkiverse.mcp.server.Tool; diff --git a/src/mcp/java/com/getpcpanel/mcp/DebugResolveTools.java b/src/mcp/java/com/getpcpanel/mcp/DebugResolveTools.java index d3c540e4..a8819bb7 100644 --- a/src/mcp/java/com/getpcpanel/mcp/DebugResolveTools.java +++ b/src/mcp/java/com/getpcpanel/mcp/DebugResolveTools.java @@ -11,12 +11,12 @@ import jakarta.inject.Inject; import com.getpcpanel.commands.Commands; -import com.getpcpanel.commands.command.AnalogBand; +import com.getpcpanel.integration.analogbands.command.AnalogBand; import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandAnalogBands; +import com.getpcpanel.integration.analogbands.command.CommandAnalogBands; import com.getpcpanel.device.Device; -import com.getpcpanel.hid.BrightnessService; -import com.getpcpanel.hid.DeviceHolder; +import com.getpcpanel.integration.device.BrightnessService; +import com.getpcpanel.device.DeviceHolder; import com.getpcpanel.profile.BaseLayerService; import com.getpcpanel.profile.DeviceSave; import com.getpcpanel.profile.Profile; diff --git a/src/mcp/java/com/getpcpanel/mcp/DeviceIntrospectionTools.java b/src/mcp/java/com/getpcpanel/mcp/DeviceIntrospectionTools.java index b8552486..5bb6ea68 100644 --- a/src/mcp/java/com/getpcpanel/mcp/DeviceIntrospectionTools.java +++ b/src/mcp/java/com/getpcpanel/mcp/DeviceIntrospectionTools.java @@ -5,12 +5,12 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import com.getpcpanel.hid.DeviceHolder; +import com.getpcpanel.device.DeviceHolder; import com.getpcpanel.profile.SaveService; -import com.getpcpanel.rest.DeviceResource; -import com.getpcpanel.rest.MidiResource; -import com.getpcpanel.rest.ProVisualColorsService; -import com.getpcpanel.rest.SerialResource; +import com.getpcpanel.device.rest.DeviceResource; +import com.getpcpanel.device.rest.MidiResource; +import com.getpcpanel.device.provider.pcpanel.ProVisualColorsService; +import com.getpcpanel.device.rest.SerialResource; import com.getpcpanel.rest.model.dto.DeviceDto; import com.getpcpanel.rest.model.dto.DeviceSnapshotDto; import com.getpcpanel.rest.model.dto.MidiDeviceDto; diff --git a/src/mcp/java/com/getpcpanel/mcp/LogTools.java b/src/mcp/java/com/getpcpanel/mcp/LogTools.java index 7ac59b64..0e3d30a0 100644 --- a/src/mcp/java/com/getpcpanel/mcp/LogTools.java +++ b/src/mcp/java/com/getpcpanel/mcp/LogTools.java @@ -11,7 +11,7 @@ import org.apache.commons.lang3.StringUtils; -import com.getpcpanel.util.FileUtil; +import com.getpcpanel.util.io.FileUtil; import io.quarkus.arc.properties.IfBuildProperty; import io.quarkiverse.mcp.server.Tool; diff --git a/src/mcp/java/com/getpcpanel/mcp/RuntimeInfoTools.java b/src/mcp/java/com/getpcpanel/mcp/RuntimeInfoTools.java index b7949e57..14291758 100644 --- a/src/mcp/java/com/getpcpanel/mcp/RuntimeInfoTools.java +++ b/src/mcp/java/com/getpcpanel/mcp/RuntimeInfoTools.java @@ -9,12 +9,12 @@ import org.eclipse.microprofile.config.inject.ConfigProperty; import com.getpcpanel.device.provider.DeviceProvider; -import com.getpcpanel.mqtt.MqttService; -import com.getpcpanel.obs.OBS; +import com.getpcpanel.integration.mqtt.MqttService; +import com.getpcpanel.integration.obs.OBS; import com.getpcpanel.profile.SaveService; -import com.getpcpanel.util.FileUtil; -import com.getpcpanel.voicemeeter.Voicemeeter; -import com.getpcpanel.wavelink.WaveLinkService; +import com.getpcpanel.util.io.FileUtil; +import com.getpcpanel.integration.voicemeeter.Voicemeeter; +import com.getpcpanel.integration.wavelink.WaveLinkService; import io.quarkus.arc.properties.IfBuildProperty; import io.quarkiverse.mcp.server.Tool; diff --git a/src/mcp/java/com/getpcpanel/mcp/SimulationTools.java b/src/mcp/java/com/getpcpanel/mcp/SimulationTools.java index 752662ca..88734f68 100644 --- a/src/mcp/java/com/getpcpanel/mcp/SimulationTools.java +++ b/src/mcp/java/com/getpcpanel/mcp/SimulationTools.java @@ -11,14 +11,14 @@ import com.getpcpanel.device.descriptor.DeviceDescriptor; import com.getpcpanel.device.provider.deej.DeejProtocol; import com.getpcpanel.device.provider.midi.MidiProtocol; -import com.getpcpanel.hid.DeviceCommunicationHandler.ButtonPressEvent; -import com.getpcpanel.hid.DeviceCommunicationHandler.KnobRotateEvent; -import com.getpcpanel.hid.DeviceHolder; -import com.getpcpanel.hid.DeviceScanner.DeviceConnectedEvent; -import com.getpcpanel.hid.DeviceScanner.DeviceDisconnectedEvent; -import com.getpcpanel.obs.OBS; +import com.getpcpanel.device.provider.pcpanel.DeviceCommunicationHandler.ButtonPressEvent; +import com.getpcpanel.device.provider.pcpanel.DeviceCommunicationHandler.KnobRotateEvent; +import com.getpcpanel.device.DeviceHolder; +import com.getpcpanel.device.provider.pcpanel.DeviceScanner.DeviceConnectedEvent; +import com.getpcpanel.device.provider.pcpanel.DeviceScanner.DeviceDisconnectedEvent; +import com.getpcpanel.integration.obs.OBS; import com.getpcpanel.profile.SaveService; -import com.getpcpanel.wavelink.WaveLinkService; +import com.getpcpanel.integration.wavelink.WaveLinkService; import dev.niels.wavelink.impl.model.WaveLinkChannel; diff --git a/src/test/java/com/getpcpanel/commands/CommandSubtypeRegistrarTest.java b/src/test/java/com/getpcpanel/commands/CommandSubtypeRegistrarTest.java new file mode 100644 index 00000000..30cde49f --- /dev/null +++ b/src/test/java/com/getpcpanel/commands/CommandSubtypeRegistrarTest.java @@ -0,0 +1,55 @@ +package com.getpcpanel.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.getpcpanel.commands.command.Command; +import com.getpcpanel.integration.device.command.CommandBrightness; +import com.getpcpanel.integration.device.command.DeviceCommandModule; +import com.getpcpanel.integration.voicemeeter.command.CommandVoiceMeeterAdvanced; +import com.getpcpanel.integration.voicemeeter.command.VoiceMeeterCommandModule; + +/** + * Validates {@link CommandSubtypeRegistrar}: it turns the {@link CommandModule} contributions into + * working Jackson polymorphic registration, and provides backwards-compatible loading of the previous + * {@code _type} ids. The {@code @All} injection that feeds {@code modules} at runtime is a standard + * Quarkus mechanism used elsewhere in the app, so it is exercised here by supplying the real modules. + */ +@DisplayName("CommandSubtypeRegistrar wiring + backwards compatibility") +class CommandSubtypeRegistrarTest { + private final ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + { + var registrar = new CommandSubtypeRegistrar(); + registrar.modules = List.of(new DeviceCommandModule(), new VoiceMeeterCommandModule()); + registrar.customize(mapper); + } + + @Test + @DisplayName("the current (nice) id deserializes") + void niceIdsResolve() throws Exception { + assertInstanceOf(CommandBrightness.class, mapper.readValue("{\"_type\":\"device.brightness\"}", Command.class)); + assertInstanceOf(CommandVoiceMeeterAdvanced.class, mapper.readValue("{\"_type\":\"voicemeeter.advanced\"}", Command.class)); + } + + @Test + @DisplayName("a legacy FQCN id from an old save still loads, and re-saving converts it to the nice id") + void legacyIdsLoadAndConvert() throws Exception { + // an old profiles.json persisted the command's fully-qualified class name as _type + var loaded = mapper.readValue("{\"_type\":\"com.getpcpanel.commands.command.CommandBrightness\"}", Command.class); + assertInstanceOf(CommandBrightness.class, loaded); + + // re-saving writes the nice current id — a transparent one-way conversion, never the legacy id + var json = mapper.writeValueAsString(loaded); + assertTrue(json.contains("\"device.brightness\""), () -> "expected the nice id in: " + json); + assertFalse(json.contains("commands.command.CommandBrightness"), () -> "must not re-write the legacy id: " + json); + } +} diff --git a/src/test/java/com/getpcpanel/hid/DialValueCalculatorTest.java b/src/test/java/com/getpcpanel/commands/DialValueCalculatorTest.java similarity index 93% rename from src/test/java/com/getpcpanel/hid/DialValueCalculatorTest.java rename to src/test/java/com/getpcpanel/commands/DialValueCalculatorTest.java index b8740b6c..c8bdb19f 100644 --- a/src/test/java/com/getpcpanel/hid/DialValueCalculatorTest.java +++ b/src/test/java/com/getpcpanel/commands/DialValueCalculatorTest.java @@ -1,11 +1,11 @@ -package com.getpcpanel.hid; +package com.getpcpanel.commands; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; -import com.getpcpanel.commands.command.CommandBrightness; +import com.getpcpanel.integration.device.command.CommandBrightness; import com.getpcpanel.commands.command.DialAction.DialCommandParams; import com.getpcpanel.profile.dto.KnobSetting; import com.getpcpanel.util.Util; diff --git a/src/test/java/com/getpcpanel/hid/DialValueTest.java b/src/test/java/com/getpcpanel/commands/DialValueTest.java similarity index 93% rename from src/test/java/com/getpcpanel/hid/DialValueTest.java rename to src/test/java/com/getpcpanel/commands/DialValueTest.java index abec6cc8..0ae046e5 100644 --- a/src/test/java/com/getpcpanel/hid/DialValueTest.java +++ b/src/test/java/com/getpcpanel/commands/DialValueTest.java @@ -1,11 +1,11 @@ -package com.getpcpanel.hid; +package com.getpcpanel.commands; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import com.getpcpanel.commands.command.CommandBrightness; +import com.getpcpanel.integration.device.command.CommandBrightness; import com.getpcpanel.commands.command.DialAction.DialCommandParams; import com.getpcpanel.profile.dto.KnobSetting; diff --git a/src/test/java/com/getpcpanel/commands/command/CommandKeystrokeTest.java b/src/test/java/com/getpcpanel/commands/command/CommandKeystrokeTest.java index ea9fe960..605db557 100644 --- a/src/test/java/com/getpcpanel/commands/command/CommandKeystrokeTest.java +++ b/src/test/java/com/getpcpanel/commands/command/CommandKeystrokeTest.java @@ -6,7 +6,8 @@ import org.junit.jupiter.api.Test; import com.fasterxml.jackson.databind.ObjectMapper; -import com.getpcpanel.commands.command.CommandKeystroke.KeystrokeType; +import com.getpcpanel.integration.keyboard.command.CommandKeystroke; +import com.getpcpanel.integration.keyboard.command.CommandKeystroke.KeystrokeType; /** * Functional tests for {@link CommandKeystroke}'s two modes and its JSON contract. The execute() @@ -16,12 +17,18 @@ */ @DisplayName("CommandKeystroke key vs text modes") class CommandKeystrokeTest { + // Command polymorphism is registered per-class (@JsonTypeName) + via the CommandModule SPI at + // runtime, so a bare mapper must be told about the subtype it deserializes. private final ObjectMapper mapper = new ObjectMapper(); + { + mapper.registerSubtypes(CommandKeystroke.class); + } + @Test @DisplayName("legacy JSON without a 'type' field deserializes as a KEY keystroke") void legacyKeystrokeDefaultsToKey() throws Exception { - var json = "{\"_type\":\"com.getpcpanel.commands.command.CommandKeystroke\",\"keystroke\":\"ctrl+A\"}"; + var json = "{\"_type\":\"keyboard.keystroke\",\"keystroke\":\"ctrl+A\"}"; var cmd = (CommandKeystroke) mapper.readValue(json, Command.class); assertEquals(KeystrokeType.KEY, cmd.getType()); assertEquals("ctrl+A", cmd.getKeystroke()); diff --git a/src/test/java/com/getpcpanel/commands/command/CommandSubtypeRegistryTest.java b/src/test/java/com/getpcpanel/commands/command/CommandSubtypeRegistryTest.java new file mode 100644 index 00000000..219f53ab --- /dev/null +++ b/src/test/java/com/getpcpanel/commands/command/CommandSubtypeRegistryTest.java @@ -0,0 +1,126 @@ +package com.getpcpanel.commands.command; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.lang.reflect.Modifier; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.getpcpanel.commands.CommandModule; + +/** + * Guards the decentralized command type registry. There is intentionally no central + * {@code @JsonSubTypes} list: every concrete {@link Command} declares its own stable id with + * {@code @JsonTypeName} in its own package, and every feature module lists its own commands via the + * {@link CommandModule} CDI SPI. These tests enforce the invariants that keep that safe, so a new + * command/plugin that forgets either half fails the build rather than silently breaking at runtime: + * + *

    + *
  1. Self-identifying — every concrete command carries a unique, non-blank {@code @JsonTypeName}.
  2. + *
  3. Module-covered — the union of all {@link CommandModule#commandTypes()} equals exactly the + * set of concrete commands (none missing, none stale/duplicated).
  4. + *
  5. Resolvable — once registered, every id deserializes back to its class (the save-migration + * guard: a moved class still loads under its frozen id).
  6. + *
+ */ +@DisplayName("Decentralized Command type registry") +class CommandSubtypeRegistryTest { + private final ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + @Test + @DisplayName("every concrete command has a unique @JsonTypeName") + void everyCommandSelfIdentifies() throws Exception { + var seen = new HashSet(); + for (var c : findConcreteCommandSubtypes()) { + var ann = c.getAnnotation(JsonTypeName.class); + assertTrue(ann != null && !ann.value().isBlank(), () -> c.getName() + " is missing a @JsonTypeName id"); + assertTrue(seen.add(ann.value()), () -> "Duplicate @JsonTypeName id: " + ann.value()); + } + } + + @Test + @DisplayName("the CommandModule SPI covers exactly the concrete commands (none missing, none stale)") + void everyCommandIsCoveredByExactlyOneModule() throws Exception { + var concrete = findConcreteCommandSubtypes(); + var declared = new ArrayList>(); + for (var module : findCommandModules()) { + declared.addAll(module.commandTypes()); + } + var declaredSet = new HashSet<>(declared); + + var missing = concrete.stream().filter(c -> !declaredSet.contains(c)).map(Class::getName).sorted().toList(); + assertTrue(missing.isEmpty(), "Concrete commands not listed in any CommandModule (add them to the feature's module): " + missing); + + var stale = declaredSet.stream().filter(c -> !concrete.contains(c)).map(Class::getName).sorted().toList(); + assertTrue(stale.isEmpty(), "CommandModule lists a type that is not a concrete command: " + stale); + + assertEquals(declared.size(), declaredSet.size(), "A command is listed by more than one CommandModule"); + } + + @Test + @DisplayName("every command id deserializes back to its class once registered") + void idsResolveAfterRegistration() throws Exception { + var concrete = findConcreteCommandSubtypes(); + concrete.forEach(mapper::registerSubtypes); + for (var c : concrete) { + var name = c.getAnnotation(JsonTypeName.class).value(); + try { + assertInstanceOf(c, mapper.readValue("{\"_type\":\"" + name + "\"}", Command.class), + () -> "id '" + name + "' resolved to the wrong class"); + } catch (Exception e) { + fail("id '" + name + "' (for " + c.getName() + ") no longer deserializes: " + e.getMessage()); + } + } + assertFalse(concrete.isEmpty(), "no commands discovered — scan is broken"); + } + + @SuppressWarnings("unchecked") + private static List findCommandModules() throws Exception { + var result = new ArrayList(); + for (var c : scan(c -> CommandModule.class.isAssignableFrom(c) && !c.isInterface() && !Modifier.isAbstract(c.getModifiers()))) { + result.add((CommandModule) c.getDeclaredConstructor().newInstance()); + } + return result; + } + + private static Set> findConcreteCommandSubtypes() throws Exception { + return scan(c -> Command.class.isAssignableFrom(c) && !c.isInterface() && !Modifier.isAbstract(c.getModifiers()) && c != Command.class); + } + + private static Set> scan(java.util.function.Predicate> keep) throws Exception { + var root = Path.of(Command.class.getProtectionDomain().getCodeSource().getLocation().toURI()); + var loader = CommandSubtypeRegistryTest.class.getClassLoader(); + var result = new HashSet>(); + try (Stream walk = Files.walk(root.resolve("com").resolve("getpcpanel"))) { + for (var classFile : walk.filter(p -> p.toString().endsWith(".class")).toList()) { + var binary = root.relativize(classFile).toString(); + binary = binary.substring(0, binary.length() - ".class".length()).replace(java.io.File.separatorChar, '.'); + Class clazz; + try { + clazz = Class.forName(binary, false, loader); + } catch (Throwable e) { + continue; + } + if (keep.test(clazz)) { + result.add(clazz); + } + } + } + return result; + } +} diff --git a/src/test/java/com/getpcpanel/commands/meta/CommandRegistryGeneratorTest.java b/src/test/java/com/getpcpanel/commands/meta/CommandRegistryGeneratorTest.java new file mode 100644 index 00000000..bbfbe6ad --- /dev/null +++ b/src/test/java/com/getpcpanel/commands/meta/CommandRegistryGeneratorTest.java @@ -0,0 +1,127 @@ +package com.getpcpanel.commands.meta; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.lang.reflect.Modifier; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.getpcpanel.commands.command.Command; + +/** + * Generates the frontend command registry from the {@link CommandMeta} annotations and guards that the + * committed {@code command-registry.generated.ts} stays current. + * + *

This is the build-time half of "the command registry is retrieved from Java": the authoritative + * list of assignable commands and their picker metadata (label / category / kinds / integration / icon) + * lives on each command in its own feature package, and the frontend consumes the generated file rather + * than a hand-maintained catalog. Run with {@code -Dpcpanel.generate.catalog} to (re)write the file; + * otherwise the test fails if the committed file is stale, telling you to regenerate. + */ +@DisplayName("Generated frontend command registry") +class CommandRegistryGeneratorTest { + private static final Path OUTPUT = Path.of("src/main/webui/src/app/features/commands/command-registry.generated.ts"); + + @Test + @DisplayName("command-registry.generated.ts is up to date with @CommandMeta") + void generatedRegistryIsCurrent() throws Exception { + var expected = render(collect()); + if (System.getProperty("pcpanel.generate.catalog") != null) { + Files.writeString(OUTPUT, expected); + return; + } + // Compare content, not line endings: the generator emits LF, but git's `text=auto` rewrites + // the checked-out file to CRLF on Windows (core.autocrlf), which would otherwise fail this test + // on the Windows CI runner only. Normalize both sides to LF. + var actual = Files.exists(OUTPUT) ? Files.readString(OUTPUT) : ""; + assertEquals(normalizeEol(expected), normalizeEol(actual), + "command-registry.generated.ts is stale — regenerate with: ./mvnw test -Dtest=CommandRegistryGeneratorTest -Dpcpanel.generate.catalog"); + } + + /** Each assignable command: its persisted id (@JsonTypeName) + picker metadata (@CommandMeta). */ + private static List collect() throws Exception { + var root = Path.of(Command.class.getProtectionDomain().getCodeSource().getLocation().toURI()); + var loader = CommandRegistryGeneratorTest.class.getClassLoader(); + var result = new ArrayList(); + try (Stream walk = Files.walk(root.resolve("com").resolve("getpcpanel"))) { + for (var classFile : walk.filter(p -> p.toString().endsWith(".class")).toList()) { + var binary = root.relativize(classFile).toString(); + binary = binary.substring(0, binary.length() - ".class".length()).replace(java.io.File.separatorChar, '.'); + Class c; + try { + c = Class.forName(binary, false, loader); + } catch (Throwable e) { + continue; + } + var meta = c.getAnnotation(CommandMeta.class); + if (meta == null) { + continue; + } + assertTrue(Command.class.isAssignableFrom(c) && !Modifier.isAbstract(c.getModifiers()), + () -> c.getName() + " has @CommandMeta but is not a concrete Command"); + var typeName = c.getAnnotation(JsonTypeName.class); + assertTrue(typeName != null && !typeName.value().isBlank(), + () -> c.getName() + " has @CommandMeta but no @JsonTypeName id"); + result.add(new Entry(typeName.value(), meta)); + } + } + result.sort(Comparator.comparing(Entry::type)); + return result; + } + + private static String render(List entries) { + var sb = new StringBuilder(); + sb.append("// GENERATED FILE — do not edit. Source: @CommandMeta on the command classes\n"); + sb.append("// (com.getpcpanel.**.command). Regenerate via CommandRegistryGeneratorTest.\n"); + sb.append("import { IconName } from '../../ui';\n"); + sb.append("import type { CommandCategory, CommandKind, Integration } from './command-catalog';\n\n"); + sb.append("export interface GeneratedCommand {\n"); + sb.append(" type: string;\n label: string;\n category: CommandCategory;\n"); + sb.append(" kinds: CommandKind[];\n integration?: Integration;\n icon: IconName;\n /** previous _type id(s) for joining hand-written field schemas keyed by the old id */\n legacy?: string;\n}\n\n"); + sb.append("export const GENERATED_COMMANDS: GeneratedCommand[] = [\n"); + for (var e : entries) { + sb.append(" { type: ").append(q(e.type())); + sb.append(", label: ").append(q(e.meta().label())); + sb.append(", category: ").append(q(e.meta().category().name())); + sb.append(", kinds: ["); + var ks = e.meta().kinds(); + for (var i = 0; i < ks.length; i++) { + sb.append(q(ks[i].name())); + if (i < ks.length - 1) { + sb.append(", "); + } + } + sb.append("]"); + if (!e.meta().integration().isBlank()) { + sb.append(", integration: ").append(q(e.meta().integration())); + } + sb.append(", icon: ").append(q(e.meta().icon())); + if (e.meta().legacyIds().length > 0) { + sb.append(", legacy: ").append(q(e.meta().legacyIds()[0])); + } + sb.append(" },\n"); + } + sb.append("];\n"); + return sb.toString(); + } + + private static String normalizeEol(String s) { + return s.replace("\r\n", "\n"); + } + + private static String q(String s) { + return "'" + s.replace("\\", "\\\\").replace("'", "\\'") + "'"; + } + + private record Entry(String type, CommandMeta meta) { + } +} diff --git a/src/test/java/com/getpcpanel/hid/DeviceHolderBackfillTest.java b/src/test/java/com/getpcpanel/device/DeviceHolderBackfillTest.java similarity index 90% rename from src/test/java/com/getpcpanel/hid/DeviceHolderBackfillTest.java rename to src/test/java/com/getpcpanel/device/DeviceHolderBackfillTest.java index f4f39cf6..2a0fcd6d 100644 --- a/src/test/java/com/getpcpanel/hid/DeviceHolderBackfillTest.java +++ b/src/test/java/com/getpcpanel/device/DeviceHolderBackfillTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.hid; +package com.getpcpanel.device; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -6,8 +6,8 @@ import org.junit.jupiter.api.Test; -import com.getpcpanel.device.DescriptorFactory; -import com.getpcpanel.device.DeviceType; +import com.getpcpanel.device.provider.pcpanel.DescriptorFactory; +import com.getpcpanel.device.provider.pcpanel.DeviceType; import com.getpcpanel.profile.DeviceSave; class DeviceHolderBackfillTest { diff --git a/src/test/java/com/getpcpanel/device/provider/deej/DeejSerialProviderTest.java b/src/test/java/com/getpcpanel/device/provider/deej/DeejSerialProviderTest.java index c33123ee..a0f8e1aa 100644 --- a/src/test/java/com/getpcpanel/device/provider/deej/DeejSerialProviderTest.java +++ b/src/test/java/com/getpcpanel/device/provider/deej/DeejSerialProviderTest.java @@ -19,10 +19,9 @@ import org.junit.jupiter.api.Test; import com.getpcpanel.device.descriptor.AnalogKind; -import com.getpcpanel.device.io.SerialTransport; -import com.getpcpanel.hid.DeviceCommunicationHandler.KnobRotateEvent; -import com.getpcpanel.hid.DeviceScanner.DeviceConnectedEvent; -import com.getpcpanel.hid.DeviceScanner.DeviceDisconnectedEvent; +import com.getpcpanel.device.provider.pcpanel.DeviceCommunicationHandler.KnobRotateEvent; +import com.getpcpanel.device.provider.pcpanel.DeviceScanner.DeviceConnectedEvent; +import com.getpcpanel.device.provider.pcpanel.DeviceScanner.DeviceDisconnectedEvent; import com.getpcpanel.profile.Save; import com.getpcpanel.profile.SaveService; diff --git a/src/test/java/com/getpcpanel/device/provider/midi/MidiProviderTest.java b/src/test/java/com/getpcpanel/device/provider/midi/MidiProviderTest.java index e17cce87..fc40b844 100644 --- a/src/test/java/com/getpcpanel/device/provider/midi/MidiProviderTest.java +++ b/src/test/java/com/getpcpanel/device/provider/midi/MidiProviderTest.java @@ -20,11 +20,10 @@ import org.junit.jupiter.api.Test; import com.getpcpanel.device.descriptor.AnalogKind; -import com.getpcpanel.device.io.MidiTransport; -import com.getpcpanel.hid.DeviceCommunicationHandler.ButtonPressEvent; -import com.getpcpanel.hid.DeviceCommunicationHandler.KnobRotateEvent; -import com.getpcpanel.hid.DeviceScanner.DeviceConnectedEvent; -import com.getpcpanel.hid.DeviceScanner.DeviceDisconnectedEvent; +import com.getpcpanel.device.provider.pcpanel.DeviceCommunicationHandler.ButtonPressEvent; +import com.getpcpanel.device.provider.pcpanel.DeviceCommunicationHandler.KnobRotateEvent; +import com.getpcpanel.device.provider.pcpanel.DeviceScanner.DeviceConnectedEvent; +import com.getpcpanel.device.provider.pcpanel.DeviceScanner.DeviceDisconnectedEvent; import com.getpcpanel.profile.Save; import com.getpcpanel.profile.SaveService; diff --git a/src/test/java/com/getpcpanel/hid/ByteWriterTest.java b/src/test/java/com/getpcpanel/device/provider/pcpanel/ByteWriterTest.java similarity index 98% rename from src/test/java/com/getpcpanel/hid/ByteWriterTest.java rename to src/test/java/com/getpcpanel/device/provider/pcpanel/ByteWriterTest.java index 67f12ffa..85f26c7c 100644 --- a/src/test/java/com/getpcpanel/hid/ByteWriterTest.java +++ b/src/test/java/com/getpcpanel/device/provider/pcpanel/ByteWriterTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.hid; +package com.getpcpanel.device.provider.pcpanel; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/com/getpcpanel/hid/InputInterpreterTest.java b/src/test/java/com/getpcpanel/device/provider/pcpanel/InputInterpreterTest.java similarity index 94% rename from src/test/java/com/getpcpanel/hid/InputInterpreterTest.java rename to src/test/java/com/getpcpanel/device/provider/pcpanel/InputInterpreterTest.java index 7e5e6da3..e5db862e 100644 --- a/src/test/java/com/getpcpanel/hid/InputInterpreterTest.java +++ b/src/test/java/com/getpcpanel/device/provider/pcpanel/InputInterpreterTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.hid; +package com.getpcpanel.device.provider.pcpanel; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -15,14 +15,14 @@ import com.getpcpanel.commands.Commands; import com.getpcpanel.commands.CommandsType; import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandVolumeProcessMute; -import com.getpcpanel.cpp.MuteType; -import com.getpcpanel.device.DeviceType; +import com.getpcpanel.integration.volume.command.CommandVolumeProcessMute; +import com.getpcpanel.integration.volume.platform.MuteType; +import com.getpcpanel.device.provider.pcpanel.DeviceType; import com.getpcpanel.profile.BaseLayerService; import com.getpcpanel.profile.Profile; import com.getpcpanel.profile.Save; import com.getpcpanel.profile.SaveService; -import com.getpcpanel.util.Debouncer; +import com.getpcpanel.util.concurrent.Debouncer; import jakarta.enterprise.event.Event; import jakarta.enterprise.event.NotificationOptions; diff --git a/src/test/java/com/getpcpanel/rest/ProVisualColorsServiceTest.java b/src/test/java/com/getpcpanel/device/provider/pcpanel/ProVisualColorsServiceTest.java similarity index 97% rename from src/test/java/com/getpcpanel/rest/ProVisualColorsServiceTest.java rename to src/test/java/com/getpcpanel/device/provider/pcpanel/ProVisualColorsServiceTest.java index aa70410f..c274a3b2 100644 --- a/src/test/java/com/getpcpanel/rest/ProVisualColorsServiceTest.java +++ b/src/test/java/com/getpcpanel/device/provider/pcpanel/ProVisualColorsServiceTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.rest; +package com.getpcpanel.device.provider.pcpanel; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/com/getpcpanel/cpp/ProxyRegistrationCoverageTest.java b/src/test/java/com/getpcpanel/graalvm/ProxyRegistrationCoverageTest.java similarity index 97% rename from src/test/java/com/getpcpanel/cpp/ProxyRegistrationCoverageTest.java rename to src/test/java/com/getpcpanel/graalvm/ProxyRegistrationCoverageTest.java index a90fb0a2..30f068ab 100644 --- a/src/test/java/com/getpcpanel/cpp/ProxyRegistrationCoverageTest.java +++ b/src/test/java/com/getpcpanel/graalvm/ProxyRegistrationCoverageTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp; +package com.getpcpanel.graalvm; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -18,7 +18,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.getpcpanel.cpp.linux.LinuxKeyboard; import com.sun.jna.Library; /** @@ -146,7 +145,7 @@ private static Set findProjectJnaLibraryInterfaces() throws Exception { /** The {@code target/classes} root of the MAIN sources (not {@code test-classes}), via a known main class. */ private static Path projectMainClassesRoot() throws Exception { - var location = LinuxKeyboard.class.getProtectionDomain().getCodeSource().getLocation(); + var location = com.getpcpanel.commands.command.Command.class.getProtectionDomain().getCodeSource().getLocation(); return Path.of(location.toURI()); } diff --git a/src/test/java/com/getpcpanel/analogbands/AnalogBandColorServiceTest.java b/src/test/java/com/getpcpanel/integration/analogbands/AnalogBandColorServiceTest.java similarity index 95% rename from src/test/java/com/getpcpanel/analogbands/AnalogBandColorServiceTest.java rename to src/test/java/com/getpcpanel/integration/analogbands/AnalogBandColorServiceTest.java index caa8b2bc..e87c42e7 100644 --- a/src/test/java/com/getpcpanel/analogbands/AnalogBandColorServiceTest.java +++ b/src/test/java/com/getpcpanel/integration/analogbands/AnalogBandColorServiceTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.analogbands; +package com.getpcpanel.integration.analogbands; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -11,8 +11,8 @@ import com.getpcpanel.commands.Commands; import com.getpcpanel.commands.CommandsType; -import com.getpcpanel.commands.command.AnalogBand; -import com.getpcpanel.commands.command.CommandAnalogBands; +import com.getpcpanel.integration.analogbands.command.AnalogBand; +import com.getpcpanel.integration.analogbands.command.CommandAnalogBands; import com.getpcpanel.profile.dto.LightingConfig; import com.getpcpanel.profile.dto.LightingConfig.LightingMode; diff --git a/src/test/java/com/getpcpanel/commands/command/CommandAnalogBandsTest.java b/src/test/java/com/getpcpanel/integration/analogbands/command/CommandAnalogBandsTest.java similarity index 96% rename from src/test/java/com/getpcpanel/commands/command/CommandAnalogBandsTest.java rename to src/test/java/com/getpcpanel/integration/analogbands/command/CommandAnalogBandsTest.java index d1a10263..221dfb43 100644 --- a/src/test/java/com/getpcpanel/commands/command/CommandAnalogBandsTest.java +++ b/src/test/java/com/getpcpanel/integration/analogbands/command/CommandAnalogBandsTest.java @@ -1,5 +1,6 @@ -package com.getpcpanel.commands.command; +package com.getpcpanel.integration.analogbands.command; +import com.getpcpanel.integration.profile.command.CommandProfile; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/src/test/java/com/getpcpanel/hid/BrightnessServiceTest.java b/src/test/java/com/getpcpanel/integration/device/BrightnessServiceTest.java similarity index 92% rename from src/test/java/com/getpcpanel/hid/BrightnessServiceTest.java rename to src/test/java/com/getpcpanel/integration/device/BrightnessServiceTest.java index d5c70807..c40f6752 100644 --- a/src/test/java/com/getpcpanel/hid/BrightnessServiceTest.java +++ b/src/test/java/com/getpcpanel/integration/device/BrightnessServiceTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.hid; +package com.getpcpanel.integration.device; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -9,8 +9,8 @@ import com.getpcpanel.commands.Commands; import com.getpcpanel.commands.CommandsType; -import com.getpcpanel.commands.command.CommandBrightness; -import com.getpcpanel.device.DeviceType; +import com.getpcpanel.integration.device.command.CommandBrightness; +import com.getpcpanel.device.provider.pcpanel.DeviceType; import com.getpcpanel.profile.Profile; class BrightnessServiceTest { diff --git a/src/test/java/com/getpcpanel/homeassistant/HaActionYamlTest.java b/src/test/java/com/getpcpanel/integration/homeassistant/HaActionYamlTest.java similarity index 97% rename from src/test/java/com/getpcpanel/homeassistant/HaActionYamlTest.java rename to src/test/java/com/getpcpanel/integration/homeassistant/HaActionYamlTest.java index 95c0c9f6..a23be682 100644 --- a/src/test/java/com/getpcpanel/homeassistant/HaActionYamlTest.java +++ b/src/test/java/com/getpcpanel/integration/homeassistant/HaActionYamlTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.homeassistant; +package com.getpcpanel.integration.homeassistant; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; diff --git a/src/test/java/com/getpcpanel/cpp/linux/LinuxKeyboardKeystrokeTest.java b/src/test/java/com/getpcpanel/integration/keyboard/platform/linux/LinuxKeyboardKeystrokeTest.java similarity index 97% rename from src/test/java/com/getpcpanel/cpp/linux/LinuxKeyboardKeystrokeTest.java rename to src/test/java/com/getpcpanel/integration/keyboard/platform/linux/LinuxKeyboardKeystrokeTest.java index 66c95800..1d968ccb 100644 --- a/src/test/java/com/getpcpanel/cpp/linux/LinuxKeyboardKeystrokeTest.java +++ b/src/test/java/com/getpcpanel/integration/keyboard/platform/linux/LinuxKeyboardKeystrokeTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.linux; +package com.getpcpanel.integration.keyboard.platform.linux; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -11,7 +11,7 @@ import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.ValueSource; -import com.getpcpanel.commands.command.CommandMedia.VolumeButton; +import com.getpcpanel.integration.keyboard.command.CommandMedia.VolumeButton; /** * Functional tests for the Linux keystroke feature's pure string -> X11 keysym mapping (the part of diff --git a/src/test/java/com/getpcpanel/cpp/linux/LinuxKeyboardNativeConfigTest.java b/src/test/java/com/getpcpanel/integration/keyboard/platform/linux/LinuxKeyboardNativeConfigTest.java similarity index 87% rename from src/test/java/com/getpcpanel/cpp/linux/LinuxKeyboardNativeConfigTest.java rename to src/test/java/com/getpcpanel/integration/keyboard/platform/linux/LinuxKeyboardNativeConfigTest.java index e091c185..bf0467c4 100644 --- a/src/test/java/com/getpcpanel/cpp/linux/LinuxKeyboardNativeConfigTest.java +++ b/src/test/java/com/getpcpanel/integration/keyboard/platform/linux/LinuxKeyboardNativeConfigTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.linux; +package com.getpcpanel.integration.keyboard.platform.linux; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @@ -8,7 +8,7 @@ import org.junit.jupiter.api.condition.EnabledOnOs; import org.junit.jupiter.api.condition.OS; -import com.getpcpanel.commands.command.CommandKeystroke; +import com.getpcpanel.integration.keyboard.command.CommandKeystroke; /** * Generation-only test: drives the real keystroke feature so the GraalVM tracing agent records the @@ -23,7 +23,7 @@ * keypress is harmless. (F24 would be quieter still but has no keycode, so it never loads libXtst.) * *

That the proxies are actually registered is guaranteed by - * {@link com.getpcpanel.cpp.ProxyRegistrationCoverageTest}; this test only makes the agent observe + * {@link com.getpcpanel.integration.volume.platform.ProxyRegistrationCoverageTest}; this test only makes the agent observe * them during generation. */ @EnabledOnOs(OS.LINUX) diff --git a/src/test/java/com/getpcpanel/cpp/osx/OsxKeyboardKeystrokeTest.java b/src/test/java/com/getpcpanel/integration/keyboard/platform/osx/OsxKeyboardKeystrokeTest.java similarity index 97% rename from src/test/java/com/getpcpanel/cpp/osx/OsxKeyboardKeystrokeTest.java rename to src/test/java/com/getpcpanel/integration/keyboard/platform/osx/OsxKeyboardKeystrokeTest.java index acc66a06..e0b7ea11 100644 --- a/src/test/java/com/getpcpanel/cpp/osx/OsxKeyboardKeystrokeTest.java +++ b/src/test/java/com/getpcpanel/integration/keyboard/platform/osx/OsxKeyboardKeystrokeTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.osx; +package com.getpcpanel.integration.keyboard.platform.osx; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; diff --git a/src/test/java/com/getpcpanel/cpp/windows/WindowsKeyboardKeystrokeTest.java b/src/test/java/com/getpcpanel/integration/keyboard/platform/windows/WindowsKeyboardKeystrokeTest.java similarity index 97% rename from src/test/java/com/getpcpanel/cpp/windows/WindowsKeyboardKeystrokeTest.java rename to src/test/java/com/getpcpanel/integration/keyboard/platform/windows/WindowsKeyboardKeystrokeTest.java index cfea43c0..87552111 100644 --- a/src/test/java/com/getpcpanel/cpp/windows/WindowsKeyboardKeystrokeTest.java +++ b/src/test/java/com/getpcpanel/integration/keyboard/platform/windows/WindowsKeyboardKeystrokeTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.windows; +package com.getpcpanel.integration.keyboard.platform.windows; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/com/getpcpanel/voicemeeter/VoicemeeterApiGuardTest.java b/src/test/java/com/getpcpanel/integration/voicemeeter/VoicemeeterApiGuardTest.java similarity index 95% rename from src/test/java/com/getpcpanel/voicemeeter/VoicemeeterApiGuardTest.java rename to src/test/java/com/getpcpanel/integration/voicemeeter/VoicemeeterApiGuardTest.java index 6b6d1dcd..f200c35e 100644 --- a/src/test/java/com/getpcpanel/voicemeeter/VoicemeeterApiGuardTest.java +++ b/src/test/java/com/getpcpanel/integration/voicemeeter/VoicemeeterApiGuardTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.voicemeeter; +package com.getpcpanel.integration.voicemeeter; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/src/test/java/com/getpcpanel/voicemeeter/VoicemeeterControlTypeTest.java b/src/test/java/com/getpcpanel/integration/voicemeeter/VoicemeeterControlTypeTest.java similarity index 93% rename from src/test/java/com/getpcpanel/voicemeeter/VoicemeeterControlTypeTest.java rename to src/test/java/com/getpcpanel/integration/voicemeeter/VoicemeeterControlTypeTest.java index 1f21912a..cb992797 100644 --- a/src/test/java/com/getpcpanel/voicemeeter/VoicemeeterControlTypeTest.java +++ b/src/test/java/com/getpcpanel/integration/voicemeeter/VoicemeeterControlTypeTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.voicemeeter; +package com.getpcpanel.integration.voicemeeter; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -8,7 +8,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import com.getpcpanel.voicemeeter.Voicemeeter.ControlType; +import com.getpcpanel.integration.voicemeeter.Voicemeeter.ControlType; /** * Pure-logic tests for {@link ControlType}, the Voicemeeter strip/bus selector. The enum's diff --git a/src/test/java/com/getpcpanel/voicemeeter/VoicemeeterNativeTest.java b/src/test/java/com/getpcpanel/integration/voicemeeter/VoicemeeterNativeTest.java similarity index 95% rename from src/test/java/com/getpcpanel/voicemeeter/VoicemeeterNativeTest.java rename to src/test/java/com/getpcpanel/integration/voicemeeter/VoicemeeterNativeTest.java index a4dc9557..97abb16d 100644 --- a/src/test/java/com/getpcpanel/voicemeeter/VoicemeeterNativeTest.java +++ b/src/test/java/com/getpcpanel/integration/voicemeeter/VoicemeeterNativeTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.voicemeeter; +package com.getpcpanel.integration.voicemeeter; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -6,9 +6,9 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import com.getpcpanel.voicemeeter.VoicemeeterInstance.tagVBVMR_AUDIOBUFFER; -import com.getpcpanel.voicemeeter.VoicemeeterInstance.tagVBVMR_AUDIOINFO; -import com.getpcpanel.voicemeeter.VoicemeeterInstance.tagVBVMR_INTERFACE; +import com.getpcpanel.integration.voicemeeter.VoicemeeterInstance.tagVBVMR_AUDIOBUFFER; +import com.getpcpanel.integration.voicemeeter.VoicemeeterInstance.tagVBVMR_AUDIOINFO; +import com.getpcpanel.integration.voicemeeter.VoicemeeterInstance.tagVBVMR_INTERFACE; import com.sun.jna.Structure; import com.sun.jna.win32.StdCallLibrary; diff --git a/src/test/java/com/getpcpanel/volume/FocusVolumeOverrideServiceTest.java b/src/test/java/com/getpcpanel/integration/volume/FocusVolumeOverrideServiceTest.java similarity index 93% rename from src/test/java/com/getpcpanel/volume/FocusVolumeOverrideServiceTest.java rename to src/test/java/com/getpcpanel/integration/volume/FocusVolumeOverrideServiceTest.java index 56c49652..5d1b05cc 100644 --- a/src/test/java/com/getpcpanel/volume/FocusVolumeOverrideServiceTest.java +++ b/src/test/java/com/getpcpanel/integration/volume/FocusVolumeOverrideServiceTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.volume; +package com.getpcpanel.integration.volume; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -13,17 +13,17 @@ import org.junit.jupiter.api.Test; import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandVolumeProcess; +import com.getpcpanel.integration.volume.command.CommandVolumeProcess; import com.getpcpanel.commands.command.DialAction; import com.getpcpanel.commands.command.DialAction.DialCommandParams; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.AudioSession; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.cpp.MuteType; +import com.getpcpanel.integration.volume.platform.AudioDevice; +import com.getpcpanel.integration.volume.platform.AudioSession; +import com.getpcpanel.integration.volume.platform.ISndCtrl; +import com.getpcpanel.integration.volume.platform.MuteType; import com.getpcpanel.profile.Save; import com.getpcpanel.profile.SaveService; -import com.getpcpanel.profile.dto.FocusVolumeOverride; -import com.getpcpanel.profile.dto.FocusVolumeTarget; +import com.getpcpanel.integration.volume.FocusVolumeOverride; +import com.getpcpanel.integration.volume.FocusVolumeTarget; /** * The decision logic of the focus-volume override service (issue #49): a focused source app redirects the diff --git a/src/test/java/com/getpcpanel/mutecolor/MuteColorServiceTest.java b/src/test/java/com/getpcpanel/integration/volume/mutecolor/MuteColorServiceTest.java similarity index 95% rename from src/test/java/com/getpcpanel/mutecolor/MuteColorServiceTest.java rename to src/test/java/com/getpcpanel/integration/volume/mutecolor/MuteColorServiceTest.java index 5c1ea1f6..da8385a9 100644 --- a/src/test/java/com/getpcpanel/mutecolor/MuteColorServiceTest.java +++ b/src/test/java/com/getpcpanel/integration/volume/mutecolor/MuteColorServiceTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.mutecolor; +package com.getpcpanel.integration.volume.mutecolor; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -15,8 +15,8 @@ import com.getpcpanel.commands.command.Command; import com.getpcpanel.profile.dto.LightingConfig; import com.getpcpanel.profile.dto.LightingConfig.LightingMode; -import com.getpcpanel.wavelink.command.CommandWaveLinkChangeLevel; -import com.getpcpanel.wavelink.command.WaveLinkCommandTarget; +import com.getpcpanel.integration.wavelink.command.CommandWaveLinkChangeLevel; +import com.getpcpanel.integration.wavelink.command.WaveLinkCommandTarget; /** * Exercises the mute-override mapping for the exact reported scenario: a Pro slider bound to a Wave diff --git a/src/test/java/com/getpcpanel/overlay/OverlayRendererColorTest.java b/src/test/java/com/getpcpanel/integration/volume/overlay/OverlayRendererColorTest.java similarity index 99% rename from src/test/java/com/getpcpanel/overlay/OverlayRendererColorTest.java rename to src/test/java/com/getpcpanel/integration/volume/overlay/OverlayRendererColorTest.java index 9f0be137..8c9829f4 100644 --- a/src/test/java/com/getpcpanel/overlay/OverlayRendererColorTest.java +++ b/src/test/java/com/getpcpanel/integration/volume/overlay/OverlayRendererColorTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.overlay; +package com.getpcpanel.integration.volume.overlay; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioVolumeTest.java b/src/test/java/com/getpcpanel/integration/volume/platform/linux/PulseAudioVolumeTest.java similarity index 96% rename from src/test/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioVolumeTest.java rename to src/test/java/com/getpcpanel/integration/volume/platform/linux/PulseAudioVolumeTest.java index 1d6e0e54..882db2da 100644 --- a/src/test/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioVolumeTest.java +++ b/src/test/java/com/getpcpanel/integration/volume/platform/linux/PulseAudioVolumeTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.linux.pulseaudio; +package com.getpcpanel.integration.volume.platform.linux; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioWrapperTest.java b/src/test/java/com/getpcpanel/integration/volume/platform/linux/PulseAudioWrapperTest.java similarity index 91% rename from src/test/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioWrapperTest.java rename to src/test/java/com/getpcpanel/integration/volume/platform/linux/PulseAudioWrapperTest.java index 966ce485..5d636e52 100644 --- a/src/test/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioWrapperTest.java +++ b/src/test/java/com/getpcpanel/integration/volume/platform/linux/PulseAudioWrapperTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.linux.pulseaudio; +package com.getpcpanel.integration.volume.platform.linux; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -7,8 +7,8 @@ import org.junit.jupiter.api.Test; -import com.getpcpanel.cpp.MuteType; -import com.getpcpanel.util.ProcessHelper; +import com.getpcpanel.integration.volume.platform.MuteType; +import com.getpcpanel.util.os.ProcessHelper; class PulseAudioWrapperTest { diff --git a/src/test/java/com/getpcpanel/cpp/linux/pulseaudio/SndCtrlPulseAudioTest.java b/src/test/java/com/getpcpanel/integration/volume/platform/linux/SndCtrlPulseAudioTest.java similarity index 97% rename from src/test/java/com/getpcpanel/cpp/linux/pulseaudio/SndCtrlPulseAudioTest.java rename to src/test/java/com/getpcpanel/integration/volume/platform/linux/SndCtrlPulseAudioTest.java index fe73962c..a0f1204c 100644 --- a/src/test/java/com/getpcpanel/cpp/linux/pulseaudio/SndCtrlPulseAudioTest.java +++ b/src/test/java/com/getpcpanel/integration/volume/platform/linux/SndCtrlPulseAudioTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.linux.pulseaudio; +package com.getpcpanel.integration.volume.platform.linux; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -10,8 +10,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import com.getpcpanel.cpp.linux.LinuxProcessHelper.ActiveWindow; -import com.getpcpanel.cpp.linux.pulseaudio.PulseAudioWrapper.PulseAudioTarget; +import com.getpcpanel.platform.process.LinuxProcessHelper.ActiveWindow; +import com.getpcpanel.integration.volume.platform.linux.PulseAudioWrapper.PulseAudioTarget; class SndCtrlPulseAudioTest { diff --git a/src/test/java/com/getpcpanel/cpp/windows/SndCtrlNativeConfigTest.java b/src/test/java/com/getpcpanel/integration/volume/platform/windows/SndCtrlNativeConfigTest.java similarity index 96% rename from src/test/java/com/getpcpanel/cpp/windows/SndCtrlNativeConfigTest.java rename to src/test/java/com/getpcpanel/integration/volume/platform/windows/SndCtrlNativeConfigTest.java index 0b742d81..78868d27 100644 --- a/src/test/java/com/getpcpanel/cpp/windows/SndCtrlNativeConfigTest.java +++ b/src/test/java/com/getpcpanel/integration/volume/platform/windows/SndCtrlNativeConfigTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.windows; +package com.getpcpanel.integration.volume.platform.windows; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -13,11 +13,11 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.AudioSession; -import com.getpcpanel.cpp.DataFlow; -import com.getpcpanel.cpp.MuteType; -import com.getpcpanel.cpp.windows.SndCtrlWindows.DefaultFor; +import com.getpcpanel.integration.volume.platform.AudioDevice; +import com.getpcpanel.integration.volume.platform.AudioSession; +import com.getpcpanel.integration.volume.platform.DataFlow; +import com.getpcpanel.integration.volume.platform.MuteType; +import com.getpcpanel.integration.volume.platform.windows.SndCtrlWindows.DefaultFor; /** * Exercises the classes involved in Windows JNI/SndCtrl integration. diff --git a/src/test/java/com/getpcpanel/wavelink/WaveLinkAppCacheTest.java b/src/test/java/com/getpcpanel/integration/wavelink/WaveLinkAppCacheTest.java similarity index 98% rename from src/test/java/com/getpcpanel/wavelink/WaveLinkAppCacheTest.java rename to src/test/java/com/getpcpanel/integration/wavelink/WaveLinkAppCacheTest.java index b567c658..afa5288c 100644 --- a/src/test/java/com/getpcpanel/wavelink/WaveLinkAppCacheTest.java +++ b/src/test/java/com/getpcpanel/integration/wavelink/WaveLinkAppCacheTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.wavelink; +package com.getpcpanel.integration.wavelink; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -13,7 +13,7 @@ import org.junit.jupiter.api.io.TempDir; import com.fasterxml.jackson.databind.ObjectMapper; -import com.getpcpanel.util.FileUtil; +import com.getpcpanel.util.io.FileUtil; import dev.niels.wavelink.impl.model.WaveLinkApp; import dev.niels.wavelink.impl.model.WaveLinkChannel; diff --git a/src/test/java/com/getpcpanel/wavelink/WaveLinkFocusResolutionTest.java b/src/test/java/com/getpcpanel/integration/wavelink/WaveLinkFocusResolutionTest.java similarity index 98% rename from src/test/java/com/getpcpanel/wavelink/WaveLinkFocusResolutionTest.java rename to src/test/java/com/getpcpanel/integration/wavelink/WaveLinkFocusResolutionTest.java index 8faf11ed..93ad4081 100644 --- a/src/test/java/com/getpcpanel/wavelink/WaveLinkFocusResolutionTest.java +++ b/src/test/java/com/getpcpanel/integration/wavelink/WaveLinkFocusResolutionTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.wavelink; +package com.getpcpanel.integration.wavelink; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -15,7 +15,7 @@ import org.junit.jupiter.api.io.TempDir; import com.fasterxml.jackson.databind.ObjectMapper; -import com.getpcpanel.util.FileUtil; +import com.getpcpanel.util.io.FileUtil; import dev.niels.wavelink.impl.model.WaveLinkApp; import dev.niels.wavelink.impl.model.WaveLinkChannel; diff --git a/src/test/java/com/getpcpanel/wavelink/WaveLinkMuteResolverTest.java b/src/test/java/com/getpcpanel/integration/wavelink/WaveLinkMuteResolverTest.java similarity index 88% rename from src/test/java/com/getpcpanel/wavelink/WaveLinkMuteResolverTest.java rename to src/test/java/com/getpcpanel/integration/wavelink/WaveLinkMuteResolverTest.java index cdcb57fe..19fc6b4a 100644 --- a/src/test/java/com/getpcpanel/wavelink/WaveLinkMuteResolverTest.java +++ b/src/test/java/com/getpcpanel/integration/wavelink/WaveLinkMuteResolverTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.wavelink; +package com.getpcpanel.integration.wavelink; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -10,12 +10,11 @@ import com.getpcpanel.commands.Commands; import com.getpcpanel.commands.CommandsType; import com.getpcpanel.commands.command.Command; -import com.getpcpanel.cpp.MuteType; -import com.getpcpanel.mutecolor.MuteStateResolver; -import com.getpcpanel.mutecolor.WaveLinkMuteResolver; -import com.getpcpanel.wavelink.command.CommandWaveLinkChangeLevel; -import com.getpcpanel.wavelink.command.CommandWaveLinkChangeMute; -import com.getpcpanel.wavelink.command.WaveLinkCommandTarget; +import com.getpcpanel.integration.volume.platform.MuteType; +import com.getpcpanel.integration.volume.mutecolor.MuteStateResolver; +import com.getpcpanel.integration.wavelink.command.CommandWaveLinkChangeLevel; +import com.getpcpanel.integration.wavelink.command.CommandWaveLinkChangeMute; +import com.getpcpanel.integration.wavelink.command.WaveLinkCommandTarget; import dev.niels.wavelink.impl.model.WaveLinkChannel; diff --git a/src/test/java/com/getpcpanel/cpp/linux/LinuxProcessHelperTest.java b/src/test/java/com/getpcpanel/platform/process/LinuxProcessHelperTest.java similarity index 95% rename from src/test/java/com/getpcpanel/cpp/linux/LinuxProcessHelperTest.java rename to src/test/java/com/getpcpanel/platform/process/LinuxProcessHelperTest.java index 9b209dc2..66f45b12 100644 --- a/src/test/java/com/getpcpanel/cpp/linux/LinuxProcessHelperTest.java +++ b/src/test/java/com/getpcpanel/platform/process/LinuxProcessHelperTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.cpp.linux; +package com.getpcpanel.platform.process; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test; -import com.getpcpanel.cpp.linux.LinuxProcessHelper.ActiveWindow; +import com.getpcpanel.platform.process.LinuxProcessHelper.ActiveWindow; class LinuxProcessHelperTest { diff --git a/src/test/java/com/getpcpanel/profile/BaseLayerServiceTest.java b/src/test/java/com/getpcpanel/profile/BaseLayerServiceTest.java index 6597827e..6baf423b 100644 --- a/src/test/java/com/getpcpanel/profile/BaseLayerServiceTest.java +++ b/src/test/java/com/getpcpanel/profile/BaseLayerServiceTest.java @@ -10,7 +10,7 @@ import com.getpcpanel.commands.Commands; import com.getpcpanel.commands.CommandsType; -import com.getpcpanel.commands.command.CommandProfile; +import com.getpcpanel.integration.profile.command.CommandProfile; import com.getpcpanel.profile.dto.LightingConfig; import com.getpcpanel.profile.dto.LightingConfig.LightingMode; import com.getpcpanel.profile.dto.SingleKnobLightingConfig.SINGLE_KNOB_MODE; diff --git a/src/test/java/com/getpcpanel/profile/SaveServiceMigrationTest.java b/src/test/java/com/getpcpanel/profile/SaveServiceMigrationTest.java index b673fb0c..4529c79c 100644 --- a/src/test/java/com/getpcpanel/profile/SaveServiceMigrationTest.java +++ b/src/test/java/com/getpcpanel/profile/SaveServiceMigrationTest.java @@ -7,8 +7,8 @@ import org.junit.jupiter.api.Test; -import com.getpcpanel.device.DescriptorFactory; -import com.getpcpanel.device.DeviceType; +import com.getpcpanel.device.provider.pcpanel.DescriptorFactory; +import com.getpcpanel.device.provider.pcpanel.DeviceType; class SaveServiceMigrationTest { @Test diff --git a/src/test/java/com/getpcpanel/rest/model/dto/DeviceDtoTest.java b/src/test/java/com/getpcpanel/rest/model/dto/DeviceDtoTest.java index e7384c36..6fddf8fb 100644 --- a/src/test/java/com/getpcpanel/rest/model/dto/DeviceDtoTest.java +++ b/src/test/java/com/getpcpanel/rest/model/dto/DeviceDtoTest.java @@ -8,8 +8,8 @@ import org.junit.jupiter.api.Test; -import com.getpcpanel.device.DescriptorFactory; -import com.getpcpanel.device.DeviceType; +import com.getpcpanel.device.provider.pcpanel.DescriptorFactory; +import com.getpcpanel.device.provider.pcpanel.DeviceType; import com.getpcpanel.profile.DeviceSave; import com.getpcpanel.profile.Save; diff --git a/src/test/java/com/getpcpanel/util/DebouncerThrottleLeadingTest.java b/src/test/java/com/getpcpanel/util/concurrent/DebouncerThrottleLeadingTest.java similarity index 98% rename from src/test/java/com/getpcpanel/util/DebouncerThrottleLeadingTest.java rename to src/test/java/com/getpcpanel/util/concurrent/DebouncerThrottleLeadingTest.java index 1b251752..fed30f16 100644 --- a/src/test/java/com/getpcpanel/util/DebouncerThrottleLeadingTest.java +++ b/src/test/java/com/getpcpanel/util/concurrent/DebouncerThrottleLeadingTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.concurrent; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/src/test/java/com/getpcpanel/util/ReconnectBackoffTest.java b/src/test/java/com/getpcpanel/util/concurrent/ReconnectBackoffTest.java similarity index 98% rename from src/test/java/com/getpcpanel/util/ReconnectBackoffTest.java rename to src/test/java/com/getpcpanel/util/concurrent/ReconnectBackoffTest.java index 6cffe119..bbcec6eb 100644 --- a/src/test/java/com/getpcpanel/util/ReconnectBackoffTest.java +++ b/src/test/java/com/getpcpanel/util/concurrent/ReconnectBackoffTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.concurrent; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/src/test/java/com/getpcpanel/util/PngEncoderTest.java b/src/test/java/com/getpcpanel/util/image/PngEncoderTest.java similarity index 98% rename from src/test/java/com/getpcpanel/util/PngEncoderTest.java rename to src/test/java/com/getpcpanel/util/image/PngEncoderTest.java index e729fa1c..f552de4d 100644 --- a/src/test/java/com/getpcpanel/util/PngEncoderTest.java +++ b/src/test/java/com/getpcpanel/util/image/PngEncoderTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.image; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/src/test/java/com/getpcpanel/util/PcPanelRootTest.java b/src/test/java/com/getpcpanel/util/io/PcPanelRootTest.java similarity index 99% rename from src/test/java/com/getpcpanel/util/PcPanelRootTest.java rename to src/test/java/com/getpcpanel/util/io/PcPanelRootTest.java index 18143081..8ed1b876 100644 --- a/src/test/java/com/getpcpanel/util/PcPanelRootTest.java +++ b/src/test/java/com/getpcpanel/util/io/PcPanelRootTest.java @@ -1,4 +1,4 @@ -package com.getpcpanel.util; +package com.getpcpanel.util.io; import static org.junit.jupiter.api.Assertions.assertEquals;