diff --git a/crates/hypercolor-core/src/effect/servo/renderer.rs b/crates/hypercolor-core/src/effect/servo/renderer.rs index 2e5001d7..672c6ce8 100644 --- a/crates/hypercolor-core/src/effect/servo/renderer.rs +++ b/crates/hypercolor-core/src/effect/servo/renderer.rs @@ -11,9 +11,7 @@ use anyhow::{Result, bail}; use hypercolor_types::canvas::{Canvas, DEFAULT_CANVAS_HEIGHT, DEFAULT_CANVAS_WIDTH, Rgba}; use hypercolor_types::display::DisplayDescriptor; -use hypercolor_types::effect::{ - ControlKind, ControlValue, EffectCategory, EffectMetadata, EffectSource, -}; +use hypercolor_types::effect::{ControlKind, ControlValue, EffectMetadata, EffectSource}; use std::collections::HashMap; use std::path::PathBuf; use std::time::Duration; @@ -374,7 +372,9 @@ fn host_driven_animation(metadata: &EffectMetadata) -> bool { #[cfg(feature = "servo-gpu-import")] fn should_reuse_cached_gpu_frame_on_no_ready(metadata: &EffectMetadata) -> bool { - metadata.category == EffectCategory::Display + // Qualified so the import doesn't go dead when servo builds without + // servo-gpu-import — this function is the only consumer. + metadata.category == hypercolor_types::effect::EffectCategory::Display } mod frame_poll; diff --git a/python/src/hypercolor/_generated/models/__init__.py b/python/src/hypercolor/_generated/models/__init__.py index 973d764e..08850d72 100644 --- a/python/src/hypercolor/_generated/models/__init__.py +++ b/python/src/hypercolor/_generated/models/__init__.py @@ -215,6 +215,7 @@ from .health_checks import HealthChecks from .health_response import HealthResponse from .identify_request import IdentifyRequest +from .input_status import InputStatus from .invoke_control_action_request import InvokeControlActionRequest from .latest_frame_status import LatestFrameStatus from .layer_order_request import LayerOrderRequest @@ -493,6 +494,7 @@ "HealthChecks", "HealthResponse", "IdentifyRequest", + "InputStatus", "InvokeControlActionRequest", "LatestFrameStatus", "LayerOrderRequest", diff --git a/python/src/hypercolor/_generated/models/api_response_system_status_data.py b/python/src/hypercolor/_generated/models/api_response_system_status_data.py index dac3d73d..ade0846b 100644 --- a/python/src/hypercolor/_generated/models/api_response_system_status_data.py +++ b/python/src/hypercolor/_generated/models/api_response_system_status_data.py @@ -10,6 +10,7 @@ if TYPE_CHECKING: from ..models.effect_health_status import EffectHealthStatus + from ..models.input_status import InputStatus from ..models.latest_frame_status import LatestFrameStatus from ..models.preview_runtime_status import PreviewRuntimeStatus from ..models.render_acceleration_status import RenderAccelerationStatus @@ -37,6 +38,12 @@ class ApiResponseSystemStatusData: effect_health (EffectHealthStatus): event_bus_subscribers (int): global_brightness (int): + input_ (InputStatus): Host keyboard/mouse capture health, for consent and remediation UX. + + `enabled` is the consent config gate. `host_capturing` is true when a + host backend is actively reading device nodes. `devices_denied` counts + input nodes present but unreadable (udev rules missing) — the signal + that distinguishes "input is off" from "input is on but blocked". preview_runtime (PreviewRuntimeStatus): render_loop (RenderLoopStatus): running (bool): @@ -62,6 +69,7 @@ class ApiResponseSystemStatusData: effect_health: EffectHealthStatus event_bus_subscribers: int global_brightness: int + input_: InputStatus preview_runtime: PreviewRuntimeStatus render_loop: RenderLoopStatus running: bool @@ -103,6 +111,8 @@ def to_dict(self) -> dict[str, Any]: global_brightness = self.global_brightness + input_ = self.input_.to_dict() + preview_runtime = self.preview_runtime.to_dict() render_loop = self.render_loop.to_dict() @@ -154,6 +164,7 @@ def to_dict(self) -> dict[str, Any]: "effect_health": effect_health, "event_bus_subscribers": event_bus_subscribers, "global_brightness": global_brightness, + "input": input_, "preview_runtime": preview_runtime, "render_loop": render_loop, "running": running, @@ -175,6 +186,7 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.effect_health_status import EffectHealthStatus + from ..models.input_status import InputStatus from ..models.latest_frame_status import LatestFrameStatus from ..models.preview_runtime_status import PreviewRuntimeStatus from ..models.render_acceleration_status import RenderAccelerationStatus @@ -210,6 +222,8 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: global_brightness = d.pop("global_brightness") + input_ = InputStatus.from_dict(d.pop("input")) + preview_runtime = PreviewRuntimeStatus.from_dict(d.pop("preview_runtime")) render_loop = RenderLoopStatus.from_dict(d.pop("render_loop")) @@ -273,6 +287,7 @@ def _parse_latest_frame(data: object) -> LatestFrameStatus | None | Unset: effect_health=effect_health, event_bus_subscribers=event_bus_subscribers, global_brightness=global_brightness, + input_=input_, preview_runtime=preview_runtime, render_loop=render_loop, running=running, diff --git a/python/src/hypercolor/_generated/models/input_status.py b/python/src/hypercolor/_generated/models/input_status.py new file mode 100644 index 00000000..6a82f7a4 --- /dev/null +++ b/python/src/hypercolor/_generated/models/input_status.py @@ -0,0 +1,107 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar, cast + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +T = TypeVar("T", bound="InputStatus") + + +@_attrs_define +class InputStatus: + """Host keyboard/mouse capture health, for consent and remediation UX. + + `enabled` is the consent config gate. `host_capturing` is true when a + host backend is actively reading device nodes. `devices_denied` counts + input nodes present but unreadable (udev rules missing) — the signal + that distinguishes "input is off" from "input is on but blocked". + + Attributes: + backends (list[str]): + devices_denied (int): + devices_opened (int): + enabled (bool): + host_capture_registered (bool): + host_capturing (bool): + """ + + backends: list[str] + devices_denied: int + devices_opened: int + enabled: bool + host_capture_registered: bool + host_capturing: bool + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + backends = self.backends + + devices_denied = self.devices_denied + + devices_opened = self.devices_opened + + enabled = self.enabled + + host_capture_registered = self.host_capture_registered + + host_capturing = self.host_capturing + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "backends": backends, + "devices_denied": devices_denied, + "devices_opened": devices_opened, + "enabled": enabled, + "host_capture_registered": host_capture_registered, + "host_capturing": host_capturing, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + backends = cast(list[str], d.pop("backends")) + + devices_denied = d.pop("devices_denied") + + devices_opened = d.pop("devices_opened") + + enabled = d.pop("enabled") + + host_capture_registered = d.pop("host_capture_registered") + + host_capturing = d.pop("host_capturing") + + input_status = cls( + backends=backends, + devices_denied=devices_denied, + devices_opened=devices_opened, + enabled=enabled, + host_capture_registered=host_capture_registered, + host_capturing=host_capturing, + ) + + input_status.additional_properties = d + return input_status + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/python/src/hypercolor/_generated/models/system_status.py b/python/src/hypercolor/_generated/models/system_status.py index eb3d7460..015e5ad6 100644 --- a/python/src/hypercolor/_generated/models/system_status.py +++ b/python/src/hypercolor/_generated/models/system_status.py @@ -10,6 +10,7 @@ if TYPE_CHECKING: from ..models.effect_health_status import EffectHealthStatus + from ..models.input_status import InputStatus from ..models.latest_frame_status import LatestFrameStatus from ..models.preview_runtime_status import PreviewRuntimeStatus from ..models.render_acceleration_status import RenderAccelerationStatus @@ -37,6 +38,12 @@ class SystemStatus: effect_health (EffectHealthStatus): event_bus_subscribers (int): global_brightness (int): + input_ (InputStatus): Host keyboard/mouse capture health, for consent and remediation UX. + + `enabled` is the consent config gate. `host_capturing` is true when a + host backend is actively reading device nodes. `devices_denied` counts + input nodes present but unreadable (udev rules missing) — the signal + that distinguishes "input is off" from "input is on but blocked". preview_runtime (PreviewRuntimeStatus): render_loop (RenderLoopStatus): running (bool): @@ -62,6 +69,7 @@ class SystemStatus: effect_health: EffectHealthStatus event_bus_subscribers: int global_brightness: int + input_: InputStatus preview_runtime: PreviewRuntimeStatus render_loop: RenderLoopStatus running: bool @@ -103,6 +111,8 @@ def to_dict(self) -> dict[str, Any]: global_brightness = self.global_brightness + input_ = self.input_.to_dict() + preview_runtime = self.preview_runtime.to_dict() render_loop = self.render_loop.to_dict() @@ -154,6 +164,7 @@ def to_dict(self) -> dict[str, Any]: "effect_health": effect_health, "event_bus_subscribers": event_bus_subscribers, "global_brightness": global_brightness, + "input": input_, "preview_runtime": preview_runtime, "render_loop": render_loop, "running": running, @@ -175,6 +186,7 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.effect_health_status import EffectHealthStatus + from ..models.input_status import InputStatus from ..models.latest_frame_status import LatestFrameStatus from ..models.preview_runtime_status import PreviewRuntimeStatus from ..models.render_acceleration_status import RenderAccelerationStatus @@ -210,6 +222,8 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: global_brightness = d.pop("global_brightness") + input_ = InputStatus.from_dict(d.pop("input")) + preview_runtime = PreviewRuntimeStatus.from_dict(d.pop("preview_runtime")) render_loop = RenderLoopStatus.from_dict(d.pop("render_loop")) @@ -273,6 +287,7 @@ def _parse_latest_frame(data: object) -> LatestFrameStatus | None | Unset: effect_health=effect_health, event_bus_subscribers=event_bus_subscribers, global_brightness=global_brightness, + input_=input_, preview_runtime=preview_runtime, render_loop=render_loop, running=running,