input capture rework

This commit is contained in:
galister 2026-07-23 15:43:47 +09:00
parent ca550f356b
commit 590980e560
36 changed files with 689 additions and 221 deletions

View File

@ -76,9 +76,13 @@
"EYE_PINCH": "Auge + Kneifen",
"NONE_INPUT_HELP": "Maus und Tastatur nicht emulieren.\nEs wird nicht möglich sein, mit Displays zu interagieren.",
"UINPUT": "Gerätesimulation",
"UINPUT_HELP": "Verwendet das uinput-Modul, um ein Gerät zu emulieren.\nIhr Benutzer muss in der Gruppe „input“ sein.\nBekannt dafür, auf den meisten Desktops gut zu funktionieren.",
"WL_VIRTUAL": "Wayland-virtueller Input",
"WL_VIRTUAL_HELP": "Sendet Ereignisse an den Wayland-Compositor unter Verwendung von\nzwlr_virtual_pointer und zwp_virtual_keyboard.\nFunktioniert nur zuverlässig auf einigen Desktops.\nFunktioniert NICHT auf KDE, GNOME oder COSMIC."
"WL_VIRTUAL_HELP": "Sendet Ereignisse an den Wayland-Compositor unter Verwendung von\nzwlr_virtual_pointer und zwp_virtual_keyboard.\nFunktioniert nur zuverlässig auf einigen Desktops.\nFunktioniert NICHT auf KDE, GNOME oder COSMIC.",
"EYE_ONLY": "Augenlaser",
"HMD_ONLY": "HMD-Laser",
"NONE_INPUT_CAPTURE_HELP": "Maus und Tastatur nicht erfassen.\nDie physische Maus oder Tastatur kann in den \ninnerhalb von WayVR gestarteten Apps nicht verwendet werden.",
"UINPUT_HELP": "Verwendet das uinput-Modul, um ein Gerät zu emulieren.\nFunktioniert auf den meisten Desktops zuverlässig.",
"EVDEV_HELP": "Eingabegeräte für die exklusive Nutzung erfassen.\nIhr Benutzer muss Mitglied der Input-Gruppe sein."
},
"AUTOSTART_APPS": "Anwendungen, die beim Start ausgeführt werden sollen",
"HANDSFREE_POINTER": "Freihändige Modus",
@ -196,7 +200,14 @@
"SAVE_PLAYSPACE_CENTER": "Playspace-Zentrum speichern",
"SAVE_PLAYSPACE_CENTER_HELP": "Nutzen Sie den aktuellen Playspace für die Zuweisung zum Zurücksetzen des Playspaces.",
"RESET_PLAYSPACE_CENTER": "Playspace-Zentrum zurücksetzen",
"RESET_PLAYSPACE_CENTER_HELP": "Zuvor festgelegtes Playspace-Zentrum zurücksetzen."
"RESET_PLAYSPACE_CENTER_HELP": "Zuvor festgelegtes Playspace-Zentrum zurücksetzen.",
"HANDSFREE_ALT_TAB": "App-Switcher-Ziel",
"HANDSFREE_ALT_TAB_HELP": "Super+X gedrückt halten, auf das gewünschte Fenster zielen und dann loslassen, um den Fokus zu wechseln.",
"INPUT_CAPTURE_METHOD": "Eingabe-Erfassungsmethode",
"POINTER_ACCELERATION": "Mausbeschleunigung",
"POINTER_ACCELERATION_HELP": "An: Adaptive Mausbeschleunigung\nAus: Konstante Geschwindigkeit",
"POINTER_SPEED": "Mausgeschwindigkeit",
"INPUT_CAPTURE_METHOD_HELP": "Ermöglicht die Nutzung physischer Peripheriegeräte innerhalb von WayVR.\n Super+Z: Eingabe zwischen WayVR und echtem Desktop umschalten\n Super+X: Zwischen WayVR-Apps umschalten"
},
"HELLO": "Hallo!",
"AUDIO": {

View File

@ -123,12 +123,14 @@
"FOCUS_FOLLOWS_MOUSE_MODE": "Mouse move on trigger touch",
"HANDSFREE_POINTER": "Handsfree mode",
"HANDSFREE_POINTER_HELP": "Input to use when motion\ncontrollers are unavailable.\nLeft pinch is grab, right is click.",
"HANDSFREE_ALT_TAB": "Handsfree alt-tab aim",
"HANDSFREE_ALT_TAB_HELP": "Hold alt-tab, aim at the desired window, then release to switch focus.",
"HANDSFREE_ALT_TAB": "App switcher aim",
"HANDSFREE_ALT_TAB_HELP": "Hold Super+X, aim at the desired window, then release to switch focus.",
"HIDE_GRAB_HELP": "Hide grab help",
"HIDE_USERNAME": "Hide username",
"INPUT_EMULATION_METHOD": "Input emulation method",
"INPUT_EMULATION_METHOD_HELP": "Try change this in case mouse or\nkeyboard input does not register.",
"INPUT_CAPTURE_METHOD": "Input capture method",
"INPUT_CAPTURE_METHOD_HELP": "Allows using physical peripherial inside WayVR.\n Super+Z: Switch input between WayVR and real desktop\n Super+X: Switch between WayVR apps",
"INPUT_PROFILES": "Change Input Bindings",
"INPUT_PROFILES_HELP": "OpenXR controller input bindings",
"INVERT_SCROLL_DIRECTION_X": "Invert horizontal scroll direction",
@ -156,12 +158,14 @@
"HMD_PINCH": "HMD + pinch",
"NONE": "None",
"NONE_INPUT_HELP": "Do not emulate mouse and keyboard.\nWill not be able to interact with screens.",
"NONE_INPUT_CAPTURE_HELP": "Do not capture mouse and keyboard.\nWill not be able to use physical mouse or\nkeyboard in apps launched inside WayVR.",
"PIPEWIRE_HELP": "Fast GPU capture,\nstandard on all desktops.",
"PW_FALLBACK_HELP": "Slow method with high CPU usage.\nTry in case PipeWire GPU doesn't work",
"SCREENCOPY_GPU_HELP": "Fast, no screen share popups.\nWorks on: Hyprland, Niri, River, Sway",
"SCREENCOPY_HELP": "Slow, no screen share popups.\nWorks on: Hyprland, Niri, River, Sway",
"UINPUT": "Device emulation",
"UINPUT_HELP": "Uses the uinput module to emulate a device.\nYour user must be in the input group.\nKnown to work well on most desktops.",
"UINPUT_HELP": "Uses the uinput module to emulate a device.\nKnown to work well on most desktops.",
"EVDEV_HELP": "Grab input devices for exclusive use.\nYour user must be in the input group.",
"WL_VIRTUAL": "Wayland virtual input",
"WL_VIRTUAL_HELP": "Send events to the Wayland compositor using\nzwlr_virtual_pointer and zwp_virtual_keyboard.\nOnly works reliably on some desktops.\nDoes NOT work on KDE, GNOME or COSMIC."
},

View File

@ -76,9 +76,13 @@
"EYE_PINCH": "Ojo + pellizco",
"NONE_INPUT_HELP": "No emular ratón y teclado.\nNo podrá interactuar con las pantallas.",
"UINPUT": "Emulación de dispositivo",
"UINPUT_HELP": "Utiliza el módulo uinput para emular un dispositivo.\nSu usuario debe estar en el grupo input.\nSe sabe que funciona bien en la mayoría de los escritorios.",
"WL_VIRTUAL": "Entrada virtual de Wayland",
"WL_VIRTUAL_HELP": "Enviar eventos al compositor de Wayland usando\nzwlr_virtual_pointer y zwp_virtual_keyboard.\nSolo funciona de forma fiable en algunos escritorios.\nNO funciona en KDE, GNOME o COSMIC."
"WL_VIRTUAL_HELP": "Enviar eventos al compositor de Wayland usando\nzwlr_virtual_pointer y zwp_virtual_keyboard.\nSolo funciona de forma fiable en algunos escritorios.\nNO funciona en KDE, GNOME o COSMIC.",
"EYE_ONLY": "Láser ocular",
"HMD_ONLY": "Láser del HMD",
"NONE_INPUT_CAPTURE_HELP": "No capturar el ratón ni el teclado.\nNo se podrá usar el ratón físico ni el\nteclado en las aplicaciones lanzadas dentro de WayVR.",
"UINPUT_HELP": "Utiliza el módulo uinput para emular un dispositivo.\nSe sabe que funciona bien en la mayoría de los escritorios.",
"EVDEV_HELP": "Captura los dispositivos de entrada para uso exclusivo.\nTu usuario debe pertenecer al grupo 'input'."
},
"AUTOSTART_APPS": "Aplicaciones a ejecutar al inicio",
"HANDSFREE_POINTER": "Modo manos libres",
@ -196,7 +200,14 @@
"SAVE_PLAYSPACE_CENTER": "Guardar centro del playspace",
"SAVE_PLAYSPACE_CENTER_HELP": "Usa el playspace actual para el comando de restablecer playspace.",
"RESET_PLAYSPACE_CENTER": "Reiniciar centro del playspace",
"RESET_PLAYSPACE_CENTER_HELP": "Borra el centro del playspace establecido previamente."
"RESET_PLAYSPACE_CENTER_HELP": "Borra el centro del playspace establecido previamente.",
"HANDSFREE_ALT_TAB": "Puntero del selector de aplicaciones",
"HANDSFREE_ALT_TAB_HELP": "Mantén presionadas Super+X, apunta a la ventana deseada y luego suelta para cambiar el foco.",
"INPUT_CAPTURE_METHOD": "Método de captura de entrada",
"POINTER_ACCELERATION": "Aceleración del ratón",
"POINTER_ACCELERATION_HELP": "Activado: Aceleración adaptativa del ratón\nDesactivado: Velocidad constante",
"POINTER_SPEED": "Velocidad del ratón",
"INPUT_CAPTURE_METHOD_HELP": "Permite usar periféricos físicos dentro de WayVR.\n Super+Z: Cambiar la entrada entre WayVR y el escritorio real\n Super+X: Cambiar entre aplicaciones de WayVR"
},
"HELLO": "¡Hola!",
"AUDIO": {

View File

@ -78,9 +78,13 @@
"EYE_PINCH": "Occhio + pizzico",
"NONE_INPUT_HELP": "Non emulare mouse e tastiera.\nNon sarà possibile interagire con gli schermi.",
"UINPUT": "Emulazione dispositivo",
"UINPUT_HELP": "Utilizza il modulo uinput per emulare un dispositivo.\nL'utente deve far parte del gruppo input.\nRisultato ottimale su quasi tutti i desktop.",
"WL_VIRTUAL": "Input virtuale Wayland",
"WL_VIRTUAL_HELP": "Invia eventi al compositor Wayland utilizzando\nzwlr_virtual_pointer e zwp_virtual_keyboard.\nFunziona in modo affidabile solo su alcuni desktop.\nNON funziona su KDE, GNOME o COSMIC."
"WL_VIRTUAL_HELP": "Invia eventi al compositor Wayland utilizzando\nzwlr_virtual_pointer e zwp_virtual_keyboard.\nFunziona in modo affidabile solo su alcuni desktop.\nNON funziona su KDE, GNOME o COSMIC.",
"EYE_ONLY": "Laser oculare",
"HMD_ONLY": "Laser HMD",
"NONE_INPUT_CAPTURE_HELP": "Non acquisire mouse e tastiera.\nNon sarà possibile utilizzare il mouse fisico o\nla tastiera nelle app lanciate all'interno di WayVR.",
"UINPUT_HELP": "Utilizza il modulo uinput per emulare un dispositivo.\nNoto per funzionare bene sulla maggior parte dei desktop.",
"EVDEV_HELP": "Acquisisci i dispositivi di input per l'uso esclusivo.\nL'utente deve appartenere al gruppo 'input'."
},
"POINTER_LERP_FACTOR": "Smussamento puntatore",
"RESTART_SOFTWARE": "Riavvia il software",
@ -217,7 +221,14 @@
"SAVE_PLAYSPACE_CENTER": "Salva centro playspace",
"SAVE_PLAYSPACE_CENTER_HELP": "Usa il playspace corrente per il comando di reset del playspace.",
"RESET_PLAYSPACE_CENTER": "Ripristina centro playspace",
"RESET_PLAYSPACE_CENTER_HELP": "Ripristina il centro del playspace impostato precedentemente."
"RESET_PLAYSPACE_CENTER_HELP": "Ripristina il centro del playspace impostato precedentemente.",
"HANDSFREE_ALT_TAB": "Mira commutatore app",
"HANDSFREE_ALT_TAB_HELP": "Tieni premuto Super+X, punta alla finestra desiderata, poi rilascia per cambiare focus.",
"INPUT_CAPTURE_METHOD": "Metodo di acquisizione input",
"POINTER_ACCELERATION": "Accelerazione del mouse",
"POINTER_ACCELERATION_HELP": "Attivo: Accelerazione adattiva del mouse\nDisattivo: Velocità costante",
"POINTER_SPEED": "Velocità mouse",
"INPUT_CAPTURE_METHOD_HELP": "Consente l'uso di periferiche fisiche all'interno di WayVR.\n Super+Z: Passa l'input tra WayVR e il desktop reale\n Super+X: Passa tra le app di WayVR"
},
"APPLICATION_LAUNCHER": "Lanciatore applicazioni",
"APPLICATION_STARTED": "Applicazione avviata",

View File

@ -76,9 +76,13 @@
"EYE_PINCH": "つまんで目を合わせる",
"NONE_INPUT_HELP": "マウスとキーボードをエミュレートしません。\nディスプレイを操作できなくなります。",
"UINPUT": "デバイス・エミュレーション",
"UINPUT_HELP": "uinput モジュールを使用してデバイスをエミュレートします。\nユーザーが input グループに属している必要があります。\nほとんどのデスクトップ環境で正常に動作することが確認されています。",
"WL_VIRTUAL": "Wayland 仮想入力",
"WL_VIRTUAL_HELP": "zwlr_virtual_pointer および zwp_virtual_keyboard を使用して、Wayland コンポジタにイベントを送信します。\n一部のデスクトップ環境でのみ、安定して動作します。\nKDE、GNOME、または COSMIC では動作しません。"
"WL_VIRTUAL_HELP": "zwlr_virtual_pointer および zwp_virtual_keyboard を使用して、Wayland コンポジタにイベントを送信します。\n一部のデスクトップ環境でのみ、安定して動作します。\nKDE、GNOME、または COSMIC では動作しません。",
"EYE_ONLY": "視線レーザー",
"HMD_ONLY": "HMDレーザー",
"NONE_INPUT_CAPTURE_HELP": "マウスとキーボードをキャプチャしない。\nWayVR内で起動したアプリで物理マウスや\nキーボードを使用できなくなります。",
"UINPUT_HELP": "uinputモジュールを使用してデバイスをエミュレートします。\nほとんどのデスクトップでうまく動作することが確認されています。",
"EVDEV_HELP": "入力デバイスを排他的に使用するために確保します。\nユーザーがinputグループに属している必要があります。"
},
"AUTOSTART_APPS": "起動時に実行するアプリ",
"HANDSFREE_POINTER": "ハンズフリーモード",
@ -141,9 +145,9 @@
"ALT_CLICK": "カスタムシェル実行 (configでalt_clickを設定)",
"SHOW_HIDE": "表示・非表示、再中心化*",
"TOGGLE_DASHBOARD": "ダッシュボードの切り替え",
"SPACE_DRAG": "Playspaceドラッグ",
"SPACE_ROTATE": "Playspace 回転",
"SPACE_RESET": "Playspace リセット",
"SPACE_DRAG": "スペースドラッグ",
"SPACE_ROTATE": "スペース回転",
"SPACE_RESET": "スペース リセット",
"CLICK_MODIFIER_RIGHT": "右クリック修飾キー",
"CLICK_MODIFIER_MIDDLE": "中クリック修飾キー",
"MOVE_MOUSE": "マウスを移動 (デフォルトでオフの場合)",
@ -161,8 +165,8 @@
"INPUT_PROFILES": "入力バインドの変更",
"INPUT_PROFILES_HELP": "OpenXRコントローラーの入力バインディング",
"NOT_SUPPORTED": "未対応",
"ENABLE_WATCH": "Watchを有効にする",
"SPACE_DRAG": "Space drag",
"ENABLE_WATCH": "ヴォッチを有効にする",
"SPACE_DRAG": "スペースムーバー",
"SPACE_GRAVITY_DAMPING": "減衰",
"SPACE_GRAVITY_DAMPING_HELP": "動きを減速させるための人工的な抵抗です。0.1 - 強い抵抗、1.0 - 抵抗なし",
"SPACE_GRAVITY_FLING_STRENGTH": "スリングの強さ",
@ -195,8 +199,15 @@
"SNAP_ANGLE_DEG_HELP": "掴んでいるウィンドウが、ピッチ軸とロール軸でこの角度にスナップします。",
"SAVE_PLAYSPACE_CENTER": "プレイスペースの中心を保存",
"SAVE_PLAYSPACE_CENTER_HELP": "現在のプレイスペースを、プレイスペースのリセットバインディングに使用します。",
"RESET_PLAYSPACE_CENTER": "playspaceの中心をリセット",
"RESET_PLAYSPACE_CENTER_HELP": "previously set playspace center をリセットします。"
"RESET_PLAYSPACE_CENTER": "スペースの中心をリセット",
"RESET_PLAYSPACE_CENTER_HELP": "スペースセンターをリセットします。",
"HANDSFREE_ALT_TAB": "アプリ切り替えの照準",
"HANDSFREE_ALT_TAB_HELP": "Super+Xを押し続け、切り替えたいウィンドウに照準を合わせ、離してフォーカスを切り替えます。",
"INPUT_CAPTURE_METHOD": "入力キャプチャ方式",
"POINTER_ACCELERATION": "マウス加速",
"POINTER_ACCELERATION_HELP": "オン: 適応型マウス加速\nオフ: 一定速度",
"POINTER_SPEED": "マウス速度",
"INPUT_CAPTURE_METHOD_HELP": "WayVR内で物理周辺機器を使用できるようにします。\n Super+Z: WayVRと実際のデスクトップ間で入力を切り替え\n Super+X: WayVRアプリ間で切り替え"
},
"HELLO": "こんにちは!",
"AUDIO": {

View File

@ -71,9 +71,13 @@
"EYE_PINCH": "Ściśnięcie palcami + oko",
"NONE_INPUT_HELP": "Nie emuluj myszy ani klawiatury.\nBrak możliwości interakcji z ekranami.",
"UINPUT": "Emulacja urządzenia",
"UINPUT_HELP": "Używa modułu uinput do emulowania urządzenia.\nTwoje konto użytkownika musi należeć do grupy input.\nWiadomo, że działa dobrze na większości komputerów stacjonarnych.",
"WL_VIRTUAL": "Wirtualne wejście Wayland",
"WL_VIRTUAL_HELP": "Wysyłaj wejście myszki/klawiatury do kompozytora Wayland za pomocą\nzwlr_virtual_pointer i zwp_virtual_keyboard.\nDziała niezawodnie tylko na niektórych pulpitach.\nNIE działa na KDE, GNOME ani COSMIC."
"WL_VIRTUAL_HELP": "Wysyłaj wejście myszki/klawiatury do kompozytora Wayland za pomocą\nzwlr_virtual_pointer i zwp_virtual_keyboard.\nDziała niezawodnie tylko na niektórych pulpitach.\nNIE działa na KDE, GNOME ani COSMIC.",
"EYE_ONLY": "Laser oczami",
"HMD_ONLY": "Laser HMD",
"NONE_INPUT_CAPTURE_HELP": "Nie przechwytuj myszy i klawiatury.\nFizyczna mysz lub klawiatura nie będą\ndostępne w aplikacjach uruchomionych w WayVR.",
"UINPUT_HELP": "Używa modułu uinput do emulacji urządzenia.\nSprawdzono, że działa dobrze na większości komputerów stacjonarnych.",
"EVDEV_HELP": "Przejmij urządzenia wejściowe do wyłącznego użytku.\nUżytkownik musi należeć do grupy 'input'."
},
"AUTOSTART_APPS": "Aplikacje auto-start",
"HANDSFREE_POINTER": "Tryb bez użycia rąk",
@ -191,7 +195,14 @@
"SAVE_PLAYSPACE_CENTER": "Zapisz centrum obszaru gry",
"SAVE_PLAYSPACE_CENTER_HELP": "Użyj aktualnego obszaru gry dla przypisania resetowania obszaru gry.",
"RESET_PLAYSPACE_CENTER": "Zresetuj środek obszaru gry",
"RESET_PLAYSPACE_CENTER_HELP": "Wyczyść poprzednio ustawiony środek obszaru playspace."
"RESET_PLAYSPACE_CENTER_HELP": "Wyczyść poprzednio ustawiony środek obszaru playspace.",
"HANDSFREE_ALT_TAB": "Cel przełącznika aplikacji",
"HANDSFREE_ALT_TAB_HELP": "Przytrzymaj Super+X, wyceluj w żądane okno, a następnie puść, aby zmienić fokus.",
"INPUT_CAPTURE_METHOD": "Metoda przechwytywania wejścia",
"POINTER_ACCELERATION": "Akceleracja myszy",
"POINTER_ACCELERATION_HELP": "Wł.: Adaptacyjna akceleracja myszy\nWył.: Stała prędkość",
"POINTER_SPEED": "Prędkość myszy",
"INPUT_CAPTURE_METHOD_HELP": "Umożliwia korzystanie z fizycznych urządzeń peryferyjnych wewnątrz WayVR.\n Super+Z: Przełącz wejście między WayVR a rzeczywistym pulpitem\n Super+X: Przełącz między aplikacjami WayVR"
},
"APPLICATION_LAUNCHER": "Uruchamiacz aplikacji",
"APPLICATIONS": "Aplikacje",

View File

@ -78,9 +78,13 @@
"EYE_PINCH": "眼睛 + 捏合",
"NONE_INPUT_HELP": "不模拟鼠标和键盘。\n将无法与屏幕进行交互。",
"UINPUT": "设备模拟",
"UINPUT_HELP": "使用 uinput 模块来模拟设备。\n您的用户必须处于 input 用户组中。\n已知在大多数桌面上运行良好。",
"WL_VIRTUAL": "Wayland 虚拟输入",
"WL_VIRTUAL_HELP": "使用 zwlr_virtual_pointer 和 zwp_virtual_keyboard 向 Wayland 合成器发送事件。\n仅在部分桌面环境下能可靠工作。\n在 KDE、GNOME 或 COSMIC 上无法工作。"
"WL_VIRTUAL_HELP": "使用 zwlr_virtual_pointer 和 zwp_virtual_keyboard 向 Wayland 合成器发送事件。\n仅在部分桌面环境下能可靠工作。\n在 KDE、GNOME 或 COSMIC 上无法工作。",
"EYE_ONLY": "眼部激光",
"HMD_ONLY": "HMD 激光指针",
"NONE_INPUT_CAPTURE_HELP": "不捕捉鼠标和键盘。\n在 WayVR 中启动的应用里将无法\n使用物理鼠标或键盘。",
"UINPUT_HELP": "使用 uinput 模块来模拟设备。\n已知在大多数桌面环境下运行良好。",
"EVDEV_HELP": "独占使用输入设备。\n您的用户必须在 input 组中。"
},
"POINTER_LERP_FACTOR": "指针平滑",
"RESTART_SOFTWARE": "重启软件",
@ -217,7 +221,14 @@
"SAVE_PLAYSPACE_CENTER": "保存 Playspace 中心",
"SAVE_PLAYSPACE_CENTER_HELP": "将当前活动空间用于重置活动空间的绑定。",
"RESET_PLAYSPACE_CENTER": "重置游玩区域中心",
"RESET_PLAYSPACE_CENTER_HELP": "清除之前设置的 Playspace 中心。"
"RESET_PLAYSPACE_CENTER_HELP": "清除之前设置的 Playspace 中心。",
"HANDSFREE_ALT_TAB": "应用切换瞄准点",
"HANDSFREE_ALT_TAB_HELP": "按住 Super+X对准目标窗口然后松开以切换焦点。",
"INPUT_CAPTURE_METHOD": "输入捕获方式",
"POINTER_ACCELERATION": "鼠标加速",
"POINTER_ACCELERATION_HELP": "开启:自适应鼠标加速\n关闭恒定速度",
"POINTER_SPEED": "鼠标速度",
"INPUT_CAPTURE_METHOD_HELP": "允许在 WayVR 中使用物理外设。\n Super+Z: 在 WayVR 和真实桌面之间切换输入\n Super+X: 在 WayVR 应用之间切换"
},
"APPLICATION_LAUNCHER": "应用启动器",
"APPLICATION_STARTED": "应用已启动",

View File

@ -319,6 +319,7 @@ pub(crate) enum SettingType {
HandsfreePointer,
HideGrabHelp,
HideUsername,
InputCaptureMethod,
InputEmulationMethod,
InvertScrollDirectionX,
InvertScrollDirectionY,
@ -436,6 +437,9 @@ impl SettingType {
Self::CaptureMethod => {
config.capture_method = wlx_common::config::CaptureMethod::from_str(value).expect("Invalid enum value!")
}
Self::InputCaptureMethod => {
config.wvr_input_capture = wlx_common::config::InputCaptureMethod::from_str(value).expect("Invalid enum value!")
}
Self::InputEmulationMethod => {
config.input_emulation_method =
wlx_common::config::InputEmulationMethod::from_str(value).expect("Invalid enum value!")
@ -460,6 +464,7 @@ impl SettingType {
fn get_enum_title(self, config: &mut GeneralConfig) -> Translation {
match self {
Self::CaptureMethod => Self::get_enum_title_inner(config.capture_method),
Self::InputCaptureMethod => Self::get_enum_title_inner(config.wvr_input_capture),
Self::InputEmulationMethod => Self::get_enum_title_inner(config.input_emulation_method),
Self::KeyboardMiddleClick => Self::get_enum_title_inner(config.keyboard_middle_click_mode),
Self::HandsfreePointer => Self::get_enum_title_inner(config.handsfree_pointer),
@ -509,6 +514,7 @@ impl SettingType {
Self::HandsfreePointer => Ok("APP_SETTINGS.HANDSFREE_POINTER"),
Self::HideGrabHelp => Ok("APP_SETTINGS.HIDE_GRAB_HELP"),
Self::HideUsername => Ok("APP_SETTINGS.HIDE_USERNAME"),
Self::InputCaptureMethod => Ok("APP_SETTINGS.INPUT_CAPTURE_METHOD"),
Self::InputEmulationMethod => Ok("APP_SETTINGS.INPUT_EMULATION_METHOD"),
Self::InvertScrollDirectionX => Ok("APP_SETTINGS.INVERT_SCROLL_DIRECTION_X"),
Self::InvertScrollDirectionY => Ok("APP_SETTINGS.INVERT_SCROLL_DIRECTION_Y"),
@ -561,6 +567,7 @@ impl SettingType {
Self::GridOpacity => Some("APP_SETTINGS.GRID_OPACITY_HELP"),
Self::HandsfreeAltTab => Some("APP_SETTINGS.HANDSFREE_ALT_TAB_HELP"),
Self::HandsfreePointer => Some("APP_SETTINGS.HANDSFREE_POINTER_HELP"),
Self::InputCaptureMethod => Some("APP_SETTINGS.INPUT_CAPTURE_METHOD_HELP"),
Self::InputEmulationMethod => Some("APP_SETTINGS.INPUT_EMULATION_METHOD_HELP"),
Self::KeyboardMiddleClick => Some("APP_SETTINGS.KEYBOARD_MIDDLE_CLICK_HELP"),
Self::LeftHandedMouse => Some("APP_SETTINGS.LEFT_HANDED_MOUSE_HELP"),

View File

@ -11,6 +11,7 @@ impl State {
pub fn mount(par: SettingsMountParams) -> anyhow::Result<State> {
let c = options_category(par.mp, par.id_parent, "APP_SETTINGS.MISC", "dashboard/blocks.svg")?;
options_dropdown::<wlx_common::config::InputEmulationMethod>(par.mp, c, &SettingType::InputEmulationMethod)?;
options_dropdown::<wlx_common::config::InputCaptureMethod>(par.mp, c, &SettingType::InputCaptureMethod)?;
options_dropdown::<wlx_common::config::CaptureMethod>(par.mp, c, &SettingType::CaptureMethod)?;
options_checkbox(par.mp, c, SettingType::XwaylandByDefault)?;
options_checkbox(par.mp, c, SettingType::UprightScreenFix)?;

View File

@ -0,0 +1,120 @@
<layout>
<elements>
<div interactable="0">
<rectangle
max_width="800"
padding="16"
flex_direction="column"
gap="8"
color="background(opacity-0.8)" border_color="outline" border="2" round="8">
<div flex_direction="column" padding="8" justify_content="space_evenly">
<label wrap="1" color="primary" weight="bold" size="20" padding_left="16" padding_right="16" translation="ALTTAB.RELEASE_TO_FOCUS" />
<!-- A simple separator -->
<rectangle width="100%" height="2" color="outline" margin="16" />
<div gap="8" flex_direction="row" >
<div width="50%" gap="8" flex_direction="column" >
<div width="100%">
<label translation="ALTTAB.WHILE_AIMING" color="primary" size="24" padding_left="16" padding_right="16" />
</div>
<div flex_direction="row" width="100%">
<div width="90">
<label text="W" color="secondary" size="20" padding_left="16" padding_right="8" />
</div>
<div flex_direction="row" wrap="1">
<label translation="ALTTAB.CLICK" color="on_background" wrap="1" size="20" padding_left="8" />
<label text="*" color="tertiary" wrap="1" size="20" padding_left="4" padding_right="16" />
</div>
</div>
<div flex_direction="row" width="100%">
<div width="90">
<label text="A" color="secondary" size="20" padding_left="16" padding_right="8" />
</div>
<div>
<label translation="ALTTAB.GRABALL" color="on_background" wrap="1" size="20" padding_left="8" padding_right="16" />
</div>
</div>
<div flex_direction="row" width="100%">
<div width="90">
<label text="Q" color="secondary" size="20" padding_left="16" padding_right="8" />
</div>
<div>
<label translation="ALTTAB.GRAB" color="on_background" wrap="1" size="20" padding_left="8" padding_right="16" />
</div>
</div>
<div flex_direction="row" width="100%">
<div width="90">
<label text="D" color="secondary" size="20" padding_left="16" padding_right="8" />
</div>
<div>
<label translation="ALTTAB.DASH" color="on_background" wrap="1" size="20" padding_left="8" padding_right="16" />
</div>
</div>
<div flex_direction="row" width="100%">
<div width="90">
<label text="E" color="secondary" size="20" padding_left="16" padding_right="8" />
</div>
<div>
<label translation="ALTTAB.EDIT" color="on_background" wrap="1" size="20" padding_left="8" padding_right="16" />
</div>
</div>
<div flex_direction="row" width="100%">
<div width="90">
<label text="R" color="secondary" size="20" padding_left="16" padding_right="8" />
</div>
<div>
<label translation="ALTTAB.RECENTER" color="on_background" wrap="1" size="20" padding_left="8" padding_right="16" />
</div>
</div>
<div flex_direction="row" width="100%">
<div width="90">
<label text="1~4" color="secondary" size="20" padding_left="16" padding_right="8" />
</div>
<div>
<label translation="ALTTAB.SET" color="on_background" wrap="1" size="20" padding_left="8" padding_right="16" />
</div>
</div>
</div>
<div width="50%" gap="8" flex_direction="column" >
<div width="100%">
<label translation="ALTTAB.GENERAL_BINDS" color="primary" size="24" padding_left="16" padding_right="16" />
</div>
<div flex_direction="row" width="100%">
<div width="90">
<label text="Super+Z" color="secondary" size="20" padding_left="16" padding_right="8" />
</div>
<div>
<label translation="ALTTAB.TOGGLE_CAPTURE" color="on_background" wrap="1" size="20" padding_left="8" padding_right="16" />
</div>
</div>
<div flex_direction="row" width="100%">
<div width="90">
<label text="Super+X" color="secondary" size="20" padding_left="16" padding_right="8" />
</div>
<div>
<label translation="ALTTAB.FOCUS_MODE" color="on_background" wrap="1" size="20" padding_left="8" padding_right="16" />
</div>
</div>
<div flex_direction="row" width="100%">
<div width="90">
<label text="Alt+F4" color="secondary" size="20" padding_left="16" padding_right="8" />
</div>
<div>
<label translation="ALTTAB.CLOSE" color="on_background" wrap="1" size="20" padding_left="8" padding_right="16" />
</div>
</div>
<!-- A simple separator -->
<rectangle width="100%" height="2" color="outline" margin="16" padding_right="16" />
<div width="100%" flex_direction="row">
<label text="*" color="tertiary" wrap="1" size="20" padding_left="8" />
<label translation="ALTTAB.MOUSE" color="on_background" wrap="1" size="20" padding_left="4" padding_right="16" />
</div>
</div>
</div>
</div>
</rectangle>
</div>
</elements>
</layout>

View File

@ -17,7 +17,9 @@
"TITLE": "Freihändig-Modus",
"NONE": "Aus",
"HMD": "HMD + Kneifen",
"EYE_TRACKING": "Auge + Kneifen"
"EYE_TRACKING": "Auge + Kneifen",
"EYE_ONLY": "Augenlaser",
"HMD_ONLY": "HMD-Laser"
}
},
"WATCH": {
@ -133,5 +135,21 @@
"WAITING_FOR_USER_DESC": "Überprüfen Sie Ihren Desktop auf ein Pop-up zur Bildschirmübertragung.\nWählen Sie Folgendes aus:",
"WAITING_FOR_USER": "Überprüfen Sie Ihren Desktop auf ein Pop-up zur Bildschirmübertragung.",
"FAIL": "Bildschirmübertragungsquelle konnte nicht initialisiert werden."
},
"ALTTAB": {
"RELEASE_TO_FOCUS": "Richten Sie den Cursor auf eine App und lassen Sie die Super-Taste los, um diese App zu fokussieren.",
"WHILE_AIMING": "Beim Zielen",
"CLICK": "VR-Pointer-Klick",
"GRABALL": "Gesamtes Set bewegen",
"GRAB": "Einzelnes Overlay bewegen",
"DASH": "Dashboard umschalten",
"EDIT": "Bearbeitungsmodus umschalten",
"SET": "Set wechseln",
"RECENTER": "Anzeigen/Ausblenden/Zentrieren",
"GENERAL_BINDS": "Allgemeine Belegungen",
"TOGGLE_CAPTURE": "Kontrolle an Desktop zurückgeben",
"FOCUS_MODE": "Fokus-Modus (Super gedrückt halten)",
"CLOSE": "Fokussierte App schließen",
"MOUSE": "Echte Mausklicks & Scrollen werden auch auf den VR-Zeiger übertragen"
}
}
}

View File

@ -78,6 +78,22 @@
"TITLE": "3D Stereo Mode"
}
},
"ALTTAB": {
"RELEASE_TO_FOCUS": "Aim at an app and release Super to focus that app.",
"WHILE_AIMING": "While aiming",
"CLICK": "VR pointer click",
"GRABALL": "Move entire set",
"GRAB": "Move individual overlay",
"DASH": "Toggle dashboard",
"EDIT": "Toggle edit mode",
"SET": "Switch set",
"RECENTER": "Show/hide/recenter",
"GENERAL_BINDS": "General bindings",
"TOGGLE_CAPTURE": "Give control back to desktop",
"FOCUS_MODE": "Focus mode (hold Super)",
"CLOSE": "Close focused app",
"MOUSE": "Real mouse clicks & scroll also translate to VR pointer"
},
"GRAB": {
"ADJUST_DISTANCE": "Adjust distance",
"ADJUST_SIZE": "Adjust size",

View File

@ -17,7 +17,9 @@
"TITLE": "Modo manos libres",
"NONE": "Desactivado",
"HMD": "HMD + pellizco",
"EYE_TRACKING": "Ojo + pellizco"
"EYE_TRACKING": "Ojo + pellizco",
"EYE_ONLY": "Láser ocular",
"HMD_ONLY": "Láser del HMD"
}
},
"WATCH": {
@ -133,5 +135,21 @@
"WAITING_FOR_USER_DESC": "Busca la ventana emergente de transmisión de pantalla en tu escritorio.\nSelecciona lo siguiente:",
"WAITING_FOR_USER": "Revisa tu escritorio para ver la ventana emergente de transmisión de pantalla.",
"FAIL": "No se pudo inicializar la fuente de transmisión de pantalla."
},
"ALTTAB": {
"RELEASE_TO_FOCUS": "Apunta a una aplicación y suelta Super para enfocar esa aplicación.",
"WHILE_AIMING": "Mientras se apunta",
"CLICK": "Clic del puntero de VR",
"GRABALL": "Mover set completo",
"GRAB": "Mover superposición individual",
"DASH": "Alternar tablero",
"EDIT": "Alternar modo de edición",
"SET": "Cambiar set",
"RECENTER": "Mostrar/ocultar/recentrar",
"GENERAL_BINDS": "Asignaciones generales",
"TOGGLE_CAPTURE": "Devolver el control al escritorio",
"FOCUS_MODE": "Modo de enfoque (mantener Super)",
"CLOSE": "Cerrar aplicación enfocada",
"MOUSE": "Los clics y el desplazamiento del ratón real también se traducen al puntero de VR"
}
}
}

View File

@ -17,7 +17,9 @@
"TITLE": "Modalità a mani libere",
"NONE": "Disattivato",
"HMD": "HMD + pizzico",
"EYE_TRACKING": "Occhio + pizzico"
"EYE_TRACKING": "Occhio + pizzico",
"EYE_ONLY": "Laser oculare",
"HMD_ONLY": "Laser HMD"
}
},
"DEFAULT": "Predefinito",
@ -131,5 +133,21 @@
"WAITING_FOR_USER_DESC": "Controlla il desktop per un pop-up di condivisione schermo.\nSeleziona quanto segue:",
"WAITING_FOR_USER": "Controlla il desktop per un pop-up di condivisione schermo.",
"FAIL": "Impossibile inizializzare la sorgente di trasmissione dello schermo."
},
"ALTTAB": {
"RELEASE_TO_FOCUS": "Mira a un'app e rilascia il tasto Super per focalizzare l'app.",
"WHILE_AIMING": "Mentre si mira",
"CLICK": "Click del puntatore VR",
"GRABALL": "Sposta l'intero set",
"GRAB": "Sposta overlay singolo",
"DASH": "Attiva/disattiva dashboard",
"EDIT": "Attiva/disattiva modalità modifica",
"SET": "Cambia set",
"RECENTER": "Mostra/nascondi/ricentra",
"GENERAL_BINDS": "Binding generali",
"TOGGLE_CAPTURE": "Restituisci il controllo al desktop",
"FOCUS_MODE": "Modalità focus (tieni premuto Super)",
"CLOSE": "Chiudi l'app focalizzata",
"MOUSE": "I clic e lo scorrimento del mouse reale vengono tradotti anche nel puntatore VR"
}
}
}

View File

@ -17,7 +17,9 @@
"TITLE": "ハンズフリーモード",
"NONE": "オフ",
"HMD": "HMD + ピンチ",
"EYE_TRACKING": "視線 + ピンチ"
"EYE_TRACKING": "視線 + ピンチ",
"EYE_ONLY": "アイルーザー",
"HMD_ONLY": "HMDレーザー"
}
},
"WATCH": {
@ -131,5 +133,21 @@
"WAITING_FOR_USER_DESC": "デスクトップのスクリーンキャストのポップアップを確認してください。\n以下を選択してください",
"WAITING_FOR_USER": "デスクトップにスクリーンキャストのポップアップが表示されていないか確認してください。",
"FAIL": "スクリーンキャストのソースを初期化できませんでした。"
},
"ALTTAB": {
"RELEASE_TO_FOCUS": "アプリを指してSuperキーを離すと、そのアプリにフォーカスします。",
"WHILE_AIMING": "照準合わせ中",
"CLICK": "VRポインタークリック",
"GRABALL": "セット全体を移動",
"GRAB": "個別のオーバーレイを移動",
"DASH": "ダッシュボードの切り替え",
"EDIT": "編集モードを切り替える",
"SET": "セットを切り替える",
"RECENTER": "表示/非表示/センター合わせ",
"GENERAL_BINDS": "一般バインド",
"TOGGLE_CAPTURE": "コントロールをデスクトップに戻す",
"FOCUS_MODE": "フォーカスモード (Superキーを長押し)",
"CLOSE": "フォーカス中のアプリを閉じる",
"MOUSE": "実際のマウスのクリックとスクロールもVRポインターに反映されます"
}
}
}

View File

@ -17,7 +17,9 @@
"TITLE": "Tryb wolnych rąk",
"NONE": "Wyłączone",
"HMD": "HMD + uszczypnięcie",
"EYE_TRACKING": "Oko + uszczypnięcie"
"EYE_TRACKING": "Oko + uszczypnięcie",
"EYE_ONLY": "Laser wzrokowy",
"HMD_ONLY": "Laser w HMD"
}
},
"WATCH": {
@ -131,5 +133,21 @@
"WAITING_FOR_USER_DESC": "Sprawdź na pulpicie okno wyboru ekranu do transmitowania.\nWybierz następujące:",
"WAITING_FOR_USER": "Sprawdź na pulpicie powiadomienie o przesyłaniu ekranu.",
"FAIL": "Nie udało się zainicjować źródła przesyłania obrazu z ekranu."
},
"ALTTAB": {
"RELEASE_TO_FOCUS": "Wyceluj w aplikację i puść klawisz Super, aby ją przenieść na pierwszy plan.",
"WHILE_AIMING": "Podczas celowania",
"CLICK": "Kliknięcie wskaźnikiem VR",
"GRABALL": "Przesuń cały zestaw",
"GRAB": "Przesuń pojedynczy overlay",
"DASH": "Przełącz pulpit sterowania",
"EDIT": "Przełącz tryb edycji",
"SET": "Zmień zestaw",
"RECENTER": "Pokaż/ukryj/wycentruj",
"GENERAL_BINDS": "Ogólne przypisania",
"TOGGLE_CAPTURE": "Oddaj kontrolę do pulpitu",
"FOCUS_MODE": "Tryb skupienia (przytrzymaj klawisz Super)",
"CLOSE": "Zamknij aktualną aplikację",
"MOUSE": "Kliknięcia i przewijanie fizycznej myszy są również przeniesione na wskaźnik VR"
}
}
}

View File

@ -17,7 +17,9 @@
"TITLE": "免提模式",
"NONE": "关闭",
"HMD": "HMD + 捏合",
"EYE_TRACKING": "眼动 + 捏合"
"EYE_TRACKING": "眼动 + 捏合",
"EYE_ONLY": "眼睛激光",
"HMD_ONLY": "头显射线"
}
},
"DEFAULT": "默认",
@ -131,5 +133,21 @@
"WAITING_FOR_USER_DESC": "请检查桌面上的屏幕投射弹出窗口。\n选择以下选项",
"WAITING_FOR_USER": "请检查桌面上的屏幕投射弹出窗口。",
"FAIL": "无法初始化屏幕镜像源。"
},
"ALTTAB": {
"RELEASE_TO_FOCUS": "瞄准一个应用程序并松开 Super 键以将焦点切换到该程序。",
"WHILE_AIMING": "瞄准时",
"CLICK": "VR 指针点击",
"GRABALL": "移动整个集",
"GRAB": "移动单个覆盖层",
"DASH": "切换控制面板",
"EDIT": "切换编辑模式",
"SET": "切换 Set",
"RECENTER": "显示/隐藏/重新中心化",
"GENERAL_BINDS": "通用绑定",
"TOGGLE_CAPTURE": "将控制权还给桌面",
"FOCUS_MODE": "焦点模式 (按住 Super 键)",
"CLOSE": "关闭当前焦点应用",
"MOUSE": "真实鼠标的点击和滚动也将转换为 VR 指针"
}
}
}

Binary file not shown.

Binary file not shown.

View File

@ -30,14 +30,10 @@ const POLL_TIMEOUT_MS: i32 = 20;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum KeyCombo {
Set1,
Set2,
Set3,
Set4,
SwitchApp,
CloseApp,
/// Always consumed by InputCapture internally
EmergencyRelease,
GrabRelease,
}
#[derive(Debug, Clone)]
@ -294,11 +290,12 @@ struct RuntimeDeviceState {
active_combos: HashSet<KeyCombo>,
horizontal_v120_remainder: f64,
vertical_v120_remainder: f64,
aiming: bool,
}
enum ProcessResult {
Continue,
EmergencyUngrab,
ForceUngrab,
ReceiverGone,
}
@ -471,7 +468,7 @@ fn worker_main(
pointer_accel,
) {
ProcessResult::Continue => {}
ProcessResult::EmergencyUngrab => {
ProcessResult::ForceUngrab => {
emergency_ungrab = true;
break;
}
@ -551,33 +548,32 @@ fn process_libinput_event(
}
if !grabbed {
// while ungrabbed, SuperTab will arm an automatic grab
// while ungrabbed, arm an automatic grab that triggers after release
if allow_deferred_grab
&& key_combo_is_pressed(KeyCombo::SwitchApp, &state.pressed_keys)
&& key_combo_is_pressed(KeyCombo::GrabRelease, &state.pressed_keys)
{
*pending_grab = true;
}
return ProcessResult::Continue;
}
if pressed && key_combo_is_pressed(KeyCombo::EmergencyRelease, &state.pressed_keys) {
log::info!("Ctrl+Alt+Del pressed, ungrabbing all input devices");
return ProcessResult::EmergencyUngrab;
} else if pressed && key_combo_is_pressed(KeyCombo::GrabRelease, &state.pressed_keys) {
log::info!("Ungrabbing all input devices");
return ProcessResult::ForceUngrab;
}
// while grabbed, these combos get emitted as normal
for combo in [
KeyCombo::SwitchApp,
KeyCombo::CloseApp,
KeyCombo::Set1,
KeyCombo::Set2,
KeyCombo::Set3,
KeyCombo::Set4,
] {
let combo_pressed = key_combo_is_pressed(combo, &state.pressed_keys);
for combo in [KeyCombo::SwitchApp, KeyCombo::CloseApp] {
let mut combo_pressed = key_combo_is_pressed(combo, &state.pressed_keys);
let was_pressed = state.active_combos.contains(&combo);
if matches!(combo, KeyCombo::SwitchApp)
&& was_pressed
&& mod_super(&state.pressed_keys)
{
// keep SwitchApp active until Super is released
combo_pressed = true;
}
if combo_pressed == was_pressed {
continue;
}
@ -792,6 +788,7 @@ fn clear_transient_state(runtime_devices: &mut HashMap<LibinputDevice, RuntimeDe
state.active_combos.clear();
state.horizontal_v120_remainder = 0.0;
state.vertical_v120_remainder = 0.0;
state.aiming = false;
}
}
@ -869,24 +866,19 @@ fn mouse_buttons() -> [KeyCode; 8] {
]
}
fn mod_alt(pressed: &HashSet<u16>) -> bool {
pressed.contains(&KeyCode::KEY_LEFTALT.0) || pressed.contains(&KeyCode::KEY_RIGHTALT.0)
}
fn mod_super(pressed: &HashSet<u16>) -> bool {
pressed.contains(&KeyCode::KEY_LEFTMETA.0) || pressed.contains(&KeyCode::KEY_RIGHTMETA.0)
}
fn key_combo_is_pressed(combo: KeyCombo, pressed: &HashSet<u16>) -> bool {
let alt =
pressed.contains(&KeyCode::KEY_LEFTALT.0) || pressed.contains(&KeyCode::KEY_RIGHTALT.0);
// let ctrl =
// pressed.contains(&KeyCode::KEY_LEFTCTRL.0) || pressed.contains(&KeyCode::KEY_RIGHTCTRL.0);
let meta =
pressed.contains(&KeyCode::KEY_LEFTMETA.0) || pressed.contains(&KeyCode::KEY_RIGHTMETA.0);
match combo {
KeyCombo::CloseApp => alt && pressed.contains(&KeyCode::KEY_F4.0),
KeyCombo::EmergencyRelease => meta && pressed.contains(&KeyCode::KEY_BACKSPACE.0),
KeyCombo::SwitchApp => meta && pressed.contains(&KeyCode::KEY_X.0),
KeyCombo::Set1 => meta && pressed.contains(&KeyCode::KEY_1.0),
KeyCombo::Set2 => meta && pressed.contains(&KeyCode::KEY_2.0),
KeyCombo::Set3 => meta && pressed.contains(&KeyCode::KEY_3.0),
KeyCombo::Set4 => meta && pressed.contains(&KeyCode::KEY_4.0),
KeyCombo::CloseApp => mod_alt(pressed) && pressed.contains(&KeyCode::KEY_F4.0),
KeyCombo::GrabRelease => mod_super(pressed) && pressed.contains(&KeyCode::KEY_Z.0),
KeyCombo::SwitchApp => mod_super(pressed) && pressed.contains(&KeyCode::KEY_X.0),
}
}

View File

@ -58,7 +58,11 @@ use wayland_protocols::xdg::shell::server::xdg_toplevel;
use wayvr_ipc::{packet_client::PositionMode, packet_server};
use wgui::{gfx::WGfx, log::LogErr};
use wlx_capture::frame::Transform;
use wlx_common::{config::GeneralConfig, desktop_finder::DesktopFinder};
use wlx_common::{
audio::{AudioSystem, SamplePlayer},
config::{GeneralConfig, InputCaptureMethod},
desktop_finder::DesktopFinder,
};
use xkbcommon::xkb;
use crate::{
@ -73,7 +77,10 @@ use crate::{
},
graphics::{ExtentExt, WGfxExtras},
ipc::{event_queue::SyncEventQueue, ipc_server, signal::WayVRSignal},
overlays::wayvr::{WvrCommand, create_wl_window_overlay},
overlays::{
anchor::ALTTAB_HELP_NAME,
wayvr::{WvrCommand, create_wl_window_overlay},
},
state::AppState,
subsystem::{
dbus::DbusConnector,
@ -139,6 +146,7 @@ pub struct WvrServerState {
overlay_to_window: SecondaryMap<OverlayID, window::WindowHandle>,
process_overlays: HashMap<process::ProcessHandle, Vec<OverlayID>>,
input_capture: Option<InputCapture>,
input_capture_init_attempted: bool,
has_input_focus: bool,
grab_toast_sent: bool,
}
@ -263,10 +271,6 @@ impl WvrServerState {
let dma_importer = ImageImporter::new(gfx);
let input_capture = InputCapture::new()
.log_err("Could not initialize evdev input capture")
.ok();
let state = Application {
output,
image_importer: dma_importer,
@ -304,7 +308,8 @@ impl WvrServerState {
window_to_overlay: HashMap::new(),
overlay_to_window: SecondaryMap::new(),
process_overlays: HashMap::new(),
input_capture,
input_capture: None,
input_capture_init_attempted: false,
has_input_focus: false,
grab_toast_sent: false,
})
@ -643,6 +648,9 @@ impl WvrServerState {
&mut app.input_state,
&mut app.tasks,
&mut app.hid_provider,
&mut app.audio_sample_player,
&mut app.audio_system,
&app.session.config,
);
wvr_server.ticks += 1;
@ -759,9 +767,42 @@ impl WvrServerState {
input_state: &mut InputState,
tasks: &mut TaskContainer,
hid_wrapper: &mut HidWrapper,
audio_sample_player: &mut SamplePlayer,
audio_system: &mut AudioSystem,
config: &GeneralConfig,
) {
let Some(input_capture) = self.input_capture.as_mut() else {
return;
let input_capture = match (self.input_capture.as_mut(), config.wvr_input_capture) {
(Some(cap), InputCaptureMethod::Evdev) => cap,
(Some(_), InputCaptureMethod::None) => {
self.input_capture.take();
return;
}
(None, InputCaptureMethod::Evdev) if !self.input_capture_init_attempted => {
self.input_capture = InputCapture::new()
.log_err("Could not initialize evdev input capture")
.ok();
self.input_capture_init_attempted = true;
if let Some(cap) = self.input_capture.as_mut() {
cap
} else {
let _ = DbusConnector::notify_send(
"Could not initialize keyboard/mouse capture!",
"Check that your user is in the input group.",
1,
5000,
0,
true,
);
return;
}
}
(None, InputCaptureMethod::Evdev) => {
return;
}
(None, InputCaptureMethod::None) => {
self.input_capture_init_attempted = false;
return;
}
};
let mut mouse_delta = DVec2::ZERO;
@ -775,21 +816,33 @@ impl WvrServerState {
if (hid::VirtualKey::Q as u32) == vk {
input_state.handsfree_state.grab = pressed;
input_state.handsfree_state.grab_float = pressed;
}
if (hid::VirtualKey::A as u32) == vk {
} else if (hid::VirtualKey::A as u32) == vk {
input_state.handsfree_state.grab = pressed;
}
if (hid::VirtualKey::W as u32) == vk {
} else if (hid::VirtualKey::W as u32) == vk {
input_state.handsfree_state.click = pressed;
}
if (hid::VirtualKey::E as u32) == vk && pressed {
} else if (hid::VirtualKey::E as u32) == vk && pressed {
tasks.enqueue(TaskType::Overlay(OverlayTask::ToggleEditMode));
}
if (hid::VirtualKey::D as u32) == vk && pressed {
} else if (hid::VirtualKey::D as u32) == vk && pressed {
tasks.enqueue(TaskType::Overlay(OverlayTask::ToggleDashboard));
} else if (hid::VirtualKey::R as u32) == vk && pressed {
tasks.enqueue(TaskType::Overlay(OverlayTask::ShowHide));
} else if (hid::VirtualKey::N1 as u32) == vk && pressed {
tasks.enqueue(TaskType::Overlay(OverlayTask::ToggleSet(0)))
} else if (hid::VirtualKey::N2 as u32) == vk && pressed {
tasks.enqueue(TaskType::Overlay(OverlayTask::ToggleSet(1)))
} else if (hid::VirtualKey::N3 as u32) == vk && pressed {
tasks.enqueue(TaskType::Overlay(OverlayTask::ToggleSet(2)))
} else if (hid::VirtualKey::N4 as u32) == vk && pressed {
tasks.enqueue(TaskType::Overlay(OverlayTask::ToggleSet(3)))
} else if (hid::VirtualKey::N5 as u32) == vk && pressed {
tasks.enqueue(TaskType::Overlay(OverlayTask::ToggleSet(4)))
} else if (hid::VirtualKey::N6 as u32) == vk && pressed {
tasks.enqueue(TaskType::Overlay(OverlayTask::ToggleSet(5)))
}
} else {
} else if self.has_input_focus {
self.manager.send_key(vk, pressed);
} else if let Some(vk) = hid::VirtualKey::from_repr(vk as u16) {
hid_wrapper.inner.send_key(vk, pressed);
}
}
input_capture::CapturedEvent::PointerButton { button, pressed } => {
@ -808,8 +861,10 @@ impl WvrServerState {
}
_ => {}
}
} else {
} else if self.has_input_focus {
self.manager.send_pointer_button(mouse_index, pressed);
} else {
hid_wrapper.inner.send_button(button as u16, pressed);
}
}
input_capture::CapturedEvent::PointerMotion {
@ -828,23 +883,42 @@ impl WvrServerState {
horizontal_v120,
vertical_v120,
} => {
let delta = WheelDelta {
let mut delta = WheelDelta {
x: horizontal_v120 as f32,
y: vertical_v120 as f32,
};
if input_state.picking_focus.is_aiming() {
input_state.handsfree_state.scroll_y = (vertical_v120 as f32) * -0.12;
} else {
} else if self.has_input_focus {
self.manager.send_pointer_axis_wheel_raw(delta);
} else {
delta.y *= -1.0;
hid_wrapper.inner.wheel(delta);
}
}
input_capture::CapturedEvent::Grabbed => {
hid_wrapper.set_input_focus(Some(self), InputFocus::WayVR);
audio_sample_player.play_sample(audio_system, "input_grab");
if !self.grab_toast_sent {
self.grab_toast_sent = true;
//TODO: toast
let _ = DbusConnector::notify_send(
"WayVR has your keyboard and mouse!",
"Super+Z to release",
1,
5000,
0,
true,
);
}
}
input_capture::CapturedEvent::Ungrabbed => {
hid_wrapper.inner.send_key(hid::VirtualKey::LSuper, false);
hid_wrapper.inner.send_key(hid::VirtualKey::RSuper, false);
self.manager.release_all_keys();
self.has_input_focus = false;
self.wm.keyboard_focus = None;
audio_sample_player.play_sample(audio_system, "input_ungrab");
}
input_capture::CapturedEvent::KeyCombo { combo, pressed } => match combo {
input_capture::KeyCombo::CloseApp if pressed => {
@ -856,9 +930,21 @@ impl WvrServerState {
input_capture::KeyCombo::SwitchApp => {
if pressed {
input_state.picking_focus = super::input::FocusPickState::Aiming;
tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Name(ALTTAB_HELP_NAME.clone()),
Box::new(move |app, o| {
o.activate(app);
}),
)));
} else {
input_state.picking_focus = super::input::FocusPickState::Picking;
tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Name(ALTTAB_HELP_NAME.clone()),
Box::new(move |_app, o| {
o.deactivate();
}),
)));
tasks.enqueue_at(
TaskType::Global(Box::new(|app| {
app.input_state.picking_focus =
@ -866,66 +952,61 @@ impl WvrServerState {
let _ = std::mem::take(&mut app.input_state.handsfree_state);
})),
Instant::now() + Duration::from_millis(33),
Instant::now() + Duration::from_millis(50),
);
}
}
input_capture::KeyCombo::Set1 if pressed => {
tasks.enqueue(TaskType::Overlay(OverlayTask::ToggleSet(0)))
}
input_capture::KeyCombo::Set2 if pressed => {
tasks.enqueue(TaskType::Overlay(OverlayTask::ToggleSet(1)))
}
input_capture::KeyCombo::Set3 if pressed => {
tasks.enqueue(TaskType::Overlay(OverlayTask::ToggleSet(2)))
}
input_capture::KeyCombo::Set4 if pressed => {
tasks.enqueue(TaskType::Overlay(OverlayTask::ToggleSet(3)))
}
_ => {}
},
}
}
if mouse_delta.length_squared() > 1e-6 {
'mouse_update: {
let Some(ref hover) = self.wm.mouse else {
break 'mouse_update;
};
if self.has_input_focus {
'mouse_update: {
let Some(ref hover) = self.wm.mouse else {
break 'mouse_update;
};
let Some(window) = self.wm.windows.get(&hover.hover_window) else {
break 'mouse_update;
};
let toplevel = window.toplevel.wl_surface().clone();
let inner_extent = with_states(&toplevel, |states| {
SurfaceBufWithImage::get_from_surface(states)
.map(|s| s.image.extent_u32arr())
.unwrap_or([1, 1])
});
let Some(hit_ctx) =
build_hit_context(&toplevel, &self.manager.state.popup_manager, inner_extent)
else {
break 'mouse_update;
};
let Some(window) = self.wm.windows.get(&hover.hover_window) else {
break 'mouse_update;
};
let toplevel = window.toplevel.wl_surface().clone();
let inner_extent = with_states(&toplevel, |states| {
SurfaceBufWithImage::get_from_surface(states)
.map(|s| s.image.extent_u32arr())
.unwrap_or([1, 1])
});
let Some(hit_ctx) = build_hit_context(
&toplevel,
&self.manager.state.popup_manager,
inner_extent,
) else {
break 'mouse_update;
};
let new_x = (hover.pos.x + mouse_delta.x).clamp(0., inner_extent[0] as f64);
let new_y = (hover.pos.y + mouse_delta.y).clamp(0., inner_extent[1] as f64);
let new_pos = DVec2::new(new_x, new_y);
let new_pos_f32 = Vec2::new(new_x as f32, new_y as f32);
let new_x = (hover.pos.x + mouse_delta.x).clamp(0., inner_extent[0] as f64);
let new_y = (hover.pos.y + mouse_delta.y).clamp(0., inner_extent[1] as f64);
let new_pos = DVec2::new(new_x, new_y);
let new_pos_f32 = Vec2::new(new_x as f32, new_y as f32);
let target = hit_ctx.hit_target_at(new_pos_f32);
let focus_target = self.hit_target_to_focus(
target.unwrap_or(WvrHitTarget::Toplevel { pos: new_pos_f32 }),
hover.hover_window,
new_pos_f32,
);
self.send_mouse_move_relative(
focus_target,
new_pos,
mouse_delta,
mouse_delta_raw,
hover.hover_window,
);
let target = hit_ctx.hit_target_at(new_pos_f32);
let focus_target = self.hit_target_to_focus(
target.unwrap_or(WvrHitTarget::Toplevel { pos: new_pos_f32 }),
hover.hover_window,
new_pos_f32,
);
self.send_mouse_move_relative(
focus_target,
new_pos,
mouse_delta,
mouse_delta_raw,
hover.hover_window,
);
}
} else {
// no has_input_focus → send back to desktop
hid_wrapper.inner.mouse_move_rel(mouse_delta);
}
}
}
@ -941,27 +1022,6 @@ impl WvrServerState {
/// Use HidWrapper::set_input_focus instead!!!
pub fn set_input_focus(&mut self, has_focus: bool) {
let Some(input_capture) = self.input_capture.as_mut() else {
return;
};
let res = input_capture
.set_grabbed(has_focus)
.log_err("Could not set input grab");
if res.is_ok() && !self.grab_toast_sent {
self.grab_toast_sent = true;
//TODO: localize
let _ = DbusConnector::notify_send(
"WayVR has your keyboard and mouse!",
"Ctrl+Alt+Del to release",
1,
5000,
0,
true,
);
}
self.has_input_focus = has_focus;
if !has_focus {
self.wm.mouse = None;

View File

@ -10,7 +10,8 @@ use wlx_common::{
astr_containers::AStrMap,
config::{
AltModifier, CaptureMethod, ChromaKeyParams, GeneralConfig, HandsfreeAltTab,
HandsfreePointer, InputEmulationMethod, SerializedWindowSet, SerializedWindowStates,
HandsfreePointer, InputCaptureMethod, InputEmulationMethod, SerializedWindowSet,
SerializedWindowStates,
},
config_io,
locale::Language,
@ -191,6 +192,7 @@ pub struct AutoSettings {
pub snap_angle_deg: f32,
pub wvr_mouse_acceleration: bool,
pub wvr_mouse_speed: f32,
pub wvr_input_capture: InputCaptureMethod,
}
fn get_settings_path() -> PathBuf {
@ -260,6 +262,7 @@ pub fn save_settings(config: &GeneralConfig) -> anyhow::Result<()> {
snap_angle_deg: config.snap_angle_deg,
wvr_mouse_acceleration: config.wvr_mouse_acceleration,
wvr_mouse_speed: config.wvr_mouse_speed,
wvr_input_capture: config.wvr_input_capture,
};
let json = serde_json::to_string_pretty(&conf).unwrap(); // want panic

View File

@ -1,4 +1,4 @@
use glam::{Affine3A, Quat, Vec3};
use glam::{Affine3A, Quat, Vec3, vec3};
use std::sync::{Arc, LazyLock};
use wgui::event::EventAlterables;
use wgui::parser::Fetchable;
@ -9,7 +9,7 @@ use crate::overlays::watch::WATCH_NAME;
use crate::state::AppState;
use crate::windowing::backend::OverlayEventData;
use crate::windowing::window::OverlayWindowConfig;
use crate::windowing::{Z_ORDER_ANCHOR, Z_ORDER_HELP};
use crate::windowing::{PIXELS_TO_METERS, Z_ORDER_ANCHOR, Z_ORDER_HELP};
pub static ANCHOR_NAME: LazyLock<Arc<str>> = LazyLock::new(|| Arc::from("anchor"));
@ -108,3 +108,29 @@ pub fn create_grab_help(app: &mut AppState) -> anyhow::Result<OverlayWindowConfi
..OverlayWindowConfig::from_backend(Box::new(panel))
})
}
pub static ALTTAB_HELP_NAME: LazyLock<Arc<str>> = LazyLock::new(|| Arc::from("alttab-help"));
pub fn create_alltab_help(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
let mut panel =
GuiPanel::new_from_template(app, "gui/alttab-help.xml", (), Default::default())?;
panel.update_layout(app)?;
Ok(OverlayWindowConfig {
name: ALTTAB_HELP_NAME.clone(),
z_order: Z_ORDER_HELP,
default_state: OverlayWindowState {
interactable: false,
grabbable: false,
transform: Affine3A::from_scale_rotation_translation(
Vec3::ONE * panel.layout.content_size.x * PIXELS_TO_METERS,
Quat::IDENTITY,
vec3(0., -0.2, -0.51), // 1 cm behind toast
),
positioning: Positioning::FollowHead { lerp: 0.1 },
..OverlayWindowState::default()
},
global: true,
..OverlayWindowConfig::from_backend(Box::new(panel))
})
}

View File

@ -3,7 +3,7 @@ use std::{
time::Instant,
};
use glam::{Affine2, Vec2, vec2};
use glam::{Affine2, DVec2, Vec2, vec2};
use wlx_capture::{WlxCapture, frame::Transform};
use crate::{
@ -314,7 +314,9 @@ impl OverlayBackend for ScreenBackend {
|| app.input_state.pointers[hit.pointer].now.move_mouse)
{
let pos = self.mouse_transform.transform_point2(hit.uv);
app.hid_provider.inner.mouse_move(pos);
app.hid_provider
.inner
.mouse_move(DVec2::new(pos.x as _, pos.y as _));
set_next_move(app.session.config.mouse_move_interval_ms as _);
}
HoverResult::consume()
@ -345,7 +347,9 @@ impl OverlayBackend for ScreenBackend {
return;
}
let pos = self.mouse_transform.transform_point2(hit.uv);
app.hid_provider.inner.mouse_move(pos);
app.hid_provider
.inner
.mouse_move(DVec2::new(pos.x as _, pos.y as _));
}
fn on_scroll(&mut self, app: &mut AppState, _hit: &PointerHit, delta: WheelDelta) {

View File

@ -1,4 +1,4 @@
use glam::vec2;
use glam::{DVec2, vec2};
use wgui::log::LogErr;
use wlx_capture::{
WlxCapture,
@ -116,10 +116,10 @@ pub fn create_screens_wayland(
app.hid_provider
.inner
.set_desktop_extent(vec2(extent.0 as f32, extent.1 as f32));
.set_desktop_extent(DVec2::new(extent.0 as _, extent.1 as _));
app.hid_provider
.inner
.set_desktop_origin(vec2(origin.0 as f32, origin.1 as f32));
.set_desktop_origin(DVec2::new(origin.0 as _, origin.1 as _));
Ok(ScreenCreateData { screens })
}

View File

@ -1,6 +1,6 @@
use std::sync::Arc;
use glam::vec2;
use glam::{DVec2, vec2};
use wlx_capture::{
WlxCapture,
frame::Transform,
@ -42,7 +42,6 @@ impl ScreenBackend {
#[cfg(feature = "pipewire")]
pub fn create_screens_x11pw(app: &mut AppState) -> anyhow::Result<ScreenCreateData> {
use glam::vec2;
use wlx_capture::xshm::xshm_get_monitors;
use wlx_common::astr_containers::AStrMapExt;
@ -91,7 +90,7 @@ pub fn create_screens_x11pw(app: &mut AppState) -> anyhow::Result<ScreenCreateDa
log::info!("Got {} monitors", monitors.len());
log::info!("Got {} streams", select_screen_result.streams.len());
let mut extent = vec2(0., 0.);
let mut extent = DVec2::ZERO;
let screens = select_screen_result
.streams
.into_iter()
@ -99,8 +98,8 @@ pub fn create_screens_x11pw(app: &mut AppState) -> anyhow::Result<ScreenCreateDa
.map(|(i, s)| {
let m = best_match(&s, monitors.iter().map(AsRef::as_ref)).unwrap();
log::info!("Stream {i} is {}", m.name);
extent.x = extent.x.max((m.monitor.x() + m.monitor.width()) as f32);
extent.y = extent.y.max((m.monitor.y() + m.monitor.height()) as f32);
extent.x = extent.x.max((m.monitor.x() + m.monitor.width()) as _);
extent.y = extent.y.max((m.monitor.y() + m.monitor.height()) as _);
let mut backend = ScreenBackend::new_raw(
m.name.clone(),
@ -133,7 +132,7 @@ pub fn create_screens_x11pw(app: &mut AppState) -> anyhow::Result<ScreenCreateDa
.collect();
app.hid_provider.inner.set_desktop_extent(extent);
app.hid_provider.inner.set_desktop_origin(vec2(0.0, 0.0));
app.hid_provider.inner.set_desktop_origin(DVec2::ZERO);
Ok(ScreenCreateData { screens })
}
@ -169,7 +168,7 @@ fn best_match<'a>(
pub fn create_screens_xshm(app: &mut AppState) -> anyhow::Result<ScreenCreateData> {
use wlx_capture::xshm::xshm_get_monitors;
let mut extent = vec2(0., 0.);
let mut extent = DVec2::ZERO;
let monitors = match xshm_get_monitors() {
Ok(m) => m,
@ -181,8 +180,8 @@ pub fn create_screens_xshm(app: &mut AppState) -> anyhow::Result<ScreenCreateDat
let screens = monitors
.into_iter()
.map(|s| {
extent.x = extent.x.max((s.monitor.x() + s.monitor.width()) as f32);
extent.y = extent.y.max((s.monitor.y() + s.monitor.height()) as f32);
extent.x = extent.x.max((s.monitor.x() + s.monitor.width()) as _);
extent.y = extent.y.max((s.monitor.y() + s.monitor.height()) as _);
let size = (s.monitor.width(), s.monitor.height());
let pos = (s.monitor.x(), s.monitor.y());
@ -216,7 +215,7 @@ pub fn create_screens_xshm(app: &mut AppState) -> anyhow::Result<ScreenCreateDat
.collect();
app.hid_provider.inner.set_desktop_extent(extent);
app.hid_provider.inner.set_desktop_origin(vec2(0.0, 0.0));
app.hid_provider.inner.set_desktop_origin(DVec2::ZERO);
Ok(ScreenCreateData { screens })
}

View File

@ -17,10 +17,9 @@ use crate::{
backend::task::{OverlayTask, SpawnPos, TaskType},
gui::panel::{GuiPanel, NewGuiPanelParams, OnCustomIdFunc},
state::AppState,
windowing::{OverlaySelector, Z_ORDER_TOAST, window::OverlayWindowConfig},
windowing::{OverlaySelector, PIXELS_TO_METERS, Z_ORDER_TOAST, window::OverlayWindowConfig},
};
const PIXELS_TO_METERS: f32 = 1. / 2000.;
static TOAST_NAME: LazyLock<Arc<str>> = LazyLock::new(|| "toast".into());
pub struct Toast {

View File

@ -148,6 +148,22 @@ impl AppState {
))?,
)?;
audio_sample_player.register_sample(
"input_grab",
audio::AudioSample::from_mp3(&audio::AudioSample::bytes_from_config_or_default(
"sound/wvr_input_capture_grabbed.mp3",
include_bytes!("assets/sound/wvr_input_capture_grabbed.mp3"),
))?,
)?;
audio_sample_player.register_sample(
"input_ungrab",
audio::AudioSample::from_mp3(&audio::AudioSample::bytes_from_config_or_default(
"sound/wvr_input_capture_ungrabbed.mp3",
include_bytes!("assets/sound/wvr_input_capture_ungrabbed.mp3"),
))?,
)?;
let mut assets = Box::new(gui::asset::GuiAsset {});
audio_sample_player.register_wgui_samples(assets.as_mut())?;

View File

@ -1,10 +1,10 @@
use glam::Vec2;
use glam::DVec2;
use idmap::{IdMap, idmap};
use idmap_derive::IntegerId;
use libc::{input_event, timeval};
use serde::Deserialize;
use std::sync::LazyLock;
use strum::{EnumIter, EnumString};
use strum::{EnumIter, EnumString, FromRepr};
use xkbcommon::xkb;
#[cfg(feature = "wayland")]
@ -27,8 +27,8 @@ struct MouseButtonAction {
#[derive(Default)]
struct MouseAction {
last_requested_pos: Option<Vec2>,
pos: Option<Vec2>,
last_requested_pos: Option<DVec2>,
pos: Option<DVec2>,
button: Option<MouseButtonAction>,
scroll: Option<WheelDelta>,
}
@ -37,7 +37,7 @@ pub const MOUSE_LEFT: u16 = 0x110;
pub const MOUSE_RIGHT: u16 = 0x111;
pub const MOUSE_MIDDLE: u16 = 0x112;
const MOUSE_EXTENT: f32 = 32768.;
const MOUSE_EXTENT: f64 = 32768.;
const EV_SYN: u16 = 0x0;
const EV_KEY: u16 = 0x1;
@ -75,7 +75,9 @@ pub const ALTGR: KeyModifier = 0x80;
#[allow(non_camel_case_types)]
#[repr(u16)]
#[derive(Debug, Deserialize, PartialEq, Eq, Clone, Copy, IntegerId, EnumString, EnumIter)]
#[derive(
Debug, Deserialize, PartialEq, Eq, Clone, Copy, IntegerId, EnumString, EnumIter, FromRepr,
)]
pub enum VirtualKey {
Escape = 9,
N1, // number row

View File

@ -1,16 +1,17 @@
use crate::overlays::toast::Toast;
use crate::subsystem::hid::provider::HidProvider;
use crate::subsystem::hid::{VirtualKey, WheelDelta, XkbKeymap};
use glam::Vec2;
use glam::DVec2;
pub struct DummyProvider;
impl HidProvider for DummyProvider {
fn mouse_move(&mut self, _pos: Vec2) {}
fn mouse_move(&mut self, _pos: DVec2) {}
fn mouse_move_rel(&mut self, _pos: DVec2) {}
fn send_button(&mut self, _button: u16, _down: bool) {}
fn wheel(&mut self, _delta: WheelDelta) {}
fn set_desktop_extent(&mut self, _extent: Vec2) {}
fn set_desktop_origin(&mut self, _origin: Vec2) {}
fn set_desktop_extent(&mut self, _extent: DVec2) {}
fn set_desktop_origin(&mut self, _origin: DVec2) {}
fn set_modifiers(&mut self, _modifiers: u8) {}
fn send_key(&self, _key: VirtualKey, _down: bool) {}

View File

@ -1,5 +1,5 @@
use crate::subsystem::hid::{VirtualKey, WheelDelta, XkbKeymap};
use glam::Vec2;
use glam::DVec2;
pub mod dummy;
pub mod uinput;
@ -7,11 +7,12 @@ pub mod wl_virtual;
pub trait HidProvider: Sync + Send {
// Pointer Functions
fn mouse_move(&mut self, pos: Vec2);
fn mouse_move(&mut self, pos: DVec2);
fn mouse_move_rel(&mut self, rel_pos: DVec2);
fn send_button(&mut self, button: u16, down: bool);
fn wheel(&mut self, delta: WheelDelta);
fn set_desktop_extent(&mut self, extent: Vec2);
fn set_desktop_origin(&mut self, origin: Vec2);
fn set_desktop_extent(&mut self, extent: DVec2);
fn set_desktop_origin(&mut self, origin: DVec2);
// Keyboard Functions
fn set_modifiers(&mut self, mods: u8);

View File

@ -4,7 +4,7 @@ use crate::subsystem::hid::{
EV_ABS, EV_KEY, EV_REL, EV_SYN, MODS_TO_KEYS, MOUSE_EXTENT, MOUSE_LEFT, MOUSE_MIDDLE,
MouseAction, MouseButtonAction, VirtualKey, WheelDelta, XkbKeymap, get_time, new_event,
};
use glam::Vec2;
use glam::DVec2;
use input_linux::{
AbsoluteAxis, AbsoluteInfo, AbsoluteInfoSetup, EventKind, InputId, Key, RelativeAxis,
UInputHandle,
@ -18,8 +18,8 @@ use wlx_common::overlays::ToastTopic;
pub struct UInputProvider {
keyboard_handle: UInputHandle<File>,
mouse_handle: UInputHandle<File>,
desktop_extent: Vec2,
desktop_origin: Vec2,
desktop_extent: DVec2,
desktop_origin: DVec2,
cur_modifiers: u8,
current_action: MouseAction,
}
@ -101,8 +101,8 @@ impl UInputProvider {
Some(Self {
keyboard_handle,
mouse_handle,
desktop_extent: Vec2::ZERO,
desktop_origin: Vec2::ZERO,
desktop_extent: DVec2::ZERO,
desktop_origin: DVec2::ZERO,
current_action: MouseAction::default(),
cur_modifiers: 0,
})
@ -117,7 +117,7 @@ impl UInputProvider {
log::error!("send_button: {res}");
}
}
fn mouse_move_internal(&mut self, pos: Vec2) {
fn mouse_move_internal(&mut self, pos: DVec2) {
#[cfg(debug_assertions)]
log::trace!("Mouse move: {pos:?}");
@ -157,12 +157,22 @@ impl UInputProvider {
}
impl HidProvider for UInputProvider {
fn mouse_move(&mut self, pos: Vec2) {
fn mouse_move(&mut self, pos: DVec2) {
if self.current_action.pos.is_none() && self.current_action.scroll.is_none() {
self.current_action.pos = Some(pos);
}
self.current_action.last_requested_pos = Some(pos);
}
fn mouse_move_rel(&mut self, rel_pos: DVec2) {
let last_pos = self.current_action.last_requested_pos.unwrap_or_default();
let new_pos = (last_pos + rel_pos).clamp(
self.desktop_origin,
self.desktop_origin + self.desktop_extent,
);
self.current_action.pos = Some(new_pos);
self.current_action.last_requested_pos = Some(new_pos);
}
fn send_button(&mut self, button: u16, down: bool) {
if self.current_action.button.is_none() {
self.current_action.button = Some(MouseButtonAction { button, down });
@ -177,10 +187,10 @@ impl HidProvider for UInputProvider {
self.current_action.pos = None;
}
}
fn set_desktop_extent(&mut self, extent: Vec2) {
fn set_desktop_extent(&mut self, extent: DVec2) {
self.desktop_extent = extent;
}
fn set_desktop_origin(&mut self, origin: Vec2) {
fn set_desktop_origin(&mut self, origin: DVec2) {
self.desktop_origin = origin;
}
fn set_modifiers(&mut self, modifiers: u8) {

View File

@ -2,7 +2,7 @@ use std::io::Write;
use std::os::fd::AsFd;
use std::time::{SystemTime, UNIX_EPOCH};
use anyhow::Context as _;
use glam::Vec2;
use glam::{DVec2};
use input_linux::sys::{*};
use smithay::reexports::rustix::fs::{memfd_create, MemfdFlags};
use smithay::reexports::wayland_protocols_wlr::virtual_pointer::v1::client::zwlr_virtual_pointer_manager_v1::ZwlrVirtualPointerManagerV1;
@ -27,18 +27,20 @@ pub struct WlVirtualProvider {
state: KbState,
virtual_pointer: ZwlrVirtualPointerV1,
desktop_extent: Vec2,
desktop_origin: Vec2,
desktop_extent: DVec2,
desktop_origin: DVec2,
keymap_file: Option<std::fs::File>,
virtual_keyboard: ZwpVirtualKeyboardV1,
keyboard_mods_state: u8,
last_pointer_position: DVec2,
}
struct KbState;
impl HidProvider for WlVirtualProvider {
fn mouse_move(&mut self, pos: Vec2) {
fn mouse_move(&mut self, pos: DVec2) {
#[cfg(debug_assertions)]
log::trace!("Pointer move: {pos:?}");
@ -51,6 +53,15 @@ impl HidProvider for WlVirtualProvider {
);
self.virtual_pointer.motion(Self::now_ms(), 0.0, 0.0);
self.virtual_pointer.frame();
self.last_pointer_position = pos;
}
fn mouse_move_rel(&mut self, rel_pos: DVec2) {
let pos = (self.last_pointer_position + rel_pos).clamp(
self.desktop_origin,
self.desktop_origin + self.desktop_extent,
);
self.mouse_move(pos);
}
fn send_button(&mut self, button: u16, down: bool) {
@ -102,11 +113,11 @@ impl HidProvider for WlVirtualProvider {
self.virtual_pointer.frame();
}
fn set_desktop_extent(&mut self, extent: Vec2) {
fn set_desktop_extent(&mut self, extent: DVec2) {
self.desktop_extent = extent;
}
fn set_desktop_origin(&mut self, origin: Vec2) {
fn set_desktop_origin(&mut self, origin: DVec2) {
self.desktop_origin = origin;
}
@ -207,9 +218,10 @@ impl WlVirtualProvider {
state,
virtual_pointer,
virtual_keyboard,
desktop_extent: Vec2::ZERO,
desktop_origin: Vec2::ZERO,
desktop_extent: DVec2::ZERO,
desktop_origin: DVec2::ZERO,
keyboard_mods_state: 0,
last_pointer_position: DVec2::ZERO,
};
result.set_keymap(&XkbKeymap {

View File

@ -19,7 +19,7 @@ use crate::{
backend::task::{CreateOverlayTask, GlobalChange, OverlayTask, SpawnPos, ToggleMode},
config::save_state,
overlays::{
anchor::{create_anchor, create_grab_help},
anchor::{create_alltab_help, create_anchor, create_grab_help},
custom::create_custom,
dashboard::{DASH_NAME, create_dash_frontend},
edit::EditWrapperManager,
@ -139,6 +139,9 @@ where
let grab_help = OverlayWindowData::from_config(create_grab_help(app)?);
me.add(grab_help, app);
let alttab_help = OverlayWindowData::from_config(create_alltab_help(app)?);
me.add(alttab_help, app);
#[cfg(feature = "whisper")]
{
use crate::overlays::whisper::create_whisper;

View File

@ -20,6 +20,8 @@ pub enum OverlaySelector {
Nothing,
}
pub const PIXELS_TO_METERS: f32 = 1. / 2000.;
pub const Z_ORDER_TOAST: u32 = 71;
pub const Z_ORDER_HELP: u32 = 70;
pub const Z_ORDER_LINES: u32 = 69;

View File

@ -62,6 +62,19 @@ pub enum InputEmulationMethod {
None,
}
#[derive(Default, Clone, Copy, Serialize, Deserialize, AsRefStr, EnumString, EnumProperty, VariantArray)]
pub enum InputCaptureMethod {
#[strum(props(Tooltip = "APP_SETTINGS.OPTION.EVDEV_HELP"))]
Evdev,
#[default]
#[strum(props(
Translation = "APP_SETTINGS.OPTION.NONE",
Tooltip = "APP_SETTINGS.OPTION.NONE_INPUT_CAPTURE_HELP"
))]
None,
}
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, AsRefStr, EnumString, EnumProperty, VariantArray)]
pub enum AltModifier {
#[default]
@ -494,4 +507,7 @@ pub struct GeneralConfig {
#[serde(default)]
pub wvr_mouse_speed: f32,
#[serde(default)]
pub wvr_input_capture: InputCaptureMethod,
}