diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..ab1f4164 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/dash-frontend/assets/lang/en.json b/dash-frontend/assets/lang/en.json index d193e624..7f022b21 100644 --- a/dash-frontend/assets/lang/en.json +++ b/dash-frontend/assets/lang/en.json @@ -227,11 +227,12 @@ "WHISPER_MODEL": "Speech-to-text model", "WHISPER_MODEL_HELP": "Whisper model to use.\nLarger models produce more accurate\nresults, but consume more VRAM.", "SWIPE_TYPE": { - "NEED_TO_DOWNLOAD_MODELS": "The swipe-to-type model needs to be downloaded.\nDownload them now?", + "NEED_TO_DOWNLOAD_MODELS": "The swipe-to-type model needs to be downloaded.\nDownload it now?", "MODELS_DOWNLOADED": "Model downloaded", "MODELS_NOT_DOWNLOADED": "Model not downloaded", "REMOVE_MODELS": "Remove swipe to type model", - "DOWNLOAD_MODELS": "Download swipe to type model ({} total)" + "DOWNLOAD_MODELS": "Download swipe to type model", + "DOWNLOAD_MODELS_HELP": "Swipe to type only works on qwerty layout keyboards" }, "SWIPE_TYPE_MODELS": "Swipe-to-type models", "SWIPE_TYPE_MODELS_HELP": "Model files used by swipe-to-type.\nAll of them are required.", diff --git a/dash-frontend/src/tab/settings/mod.rs b/dash-frontend/src/tab/settings/mod.rs index 04e8f39c..25d425dd 100644 --- a/dash-frontend/src/tab/settings/mod.rs +++ b/dash-frontend/src/tab/settings/mod.rs @@ -353,6 +353,7 @@ pub(crate) enum SettingType { InvertScrollDirectionY, KeyboardMiddleClick, KeyboardSoundEnabled, + KeyboardSwipeToTypeEnabled, Language, LeftHandedMouse, LongPressDuration, @@ -412,6 +413,7 @@ impl SettingType { Self::InvertScrollDirectionX => &mut config.invert_scroll_direction_x, Self::InvertScrollDirectionY => &mut config.invert_scroll_direction_y, Self::KeyboardSoundEnabled => &mut config.keyboard_sound_enabled, + Self::KeyboardSwipeToTypeEnabled => &mut config.keyboard_swipe_to_type_enabled, Self::LeftHandedMouse => &mut config.left_handed_mouse, Self::NotificationsEnabled => &mut config.notifications_enabled, Self::NotificationsSoundEnabled => &mut config.notifications_sound_enabled, @@ -550,6 +552,7 @@ impl SettingType { Self::InvertScrollDirectionY => Ok("APP_SETTINGS.INVERT_SCROLL_DIRECTION_Y"), Self::KeyboardMiddleClick => Ok("APP_SETTINGS.KEYBOARD_MIDDLE_CLICK"), Self::KeyboardSoundEnabled => Ok("APP_SETTINGS.KEYBOARD_SOUND_ENABLED"), + Self::KeyboardSwipeToTypeEnabled => Ok("APP_SETTINGS.KEYBOARD_SWIPE_TYPE_ENABLED"), Self::Language => Ok("APP_SETTINGS.LANGUAGE"), Self::LeftHandedMouse => Ok("APP_SETTINGS.LEFT_HANDED_MOUSE"), Self::LongPressDuration => Ok("APP_SETTINGS.LONG_PRESS_DURATION"), diff --git a/dash-frontend/src/tab/settings/tab_features.rs b/dash-frontend/src/tab/settings/tab_features.rs index e6996678..89a3edc1 100644 --- a/dash-frontend/src/tab/settings/tab_features.rs +++ b/dash-frontend/src/tab/settings/tab_features.rs @@ -1,3 +1,4 @@ +use std::path::PathBuf; use std::rc::Rc; use wgui::{ @@ -22,22 +23,23 @@ use crate::{ util::{ popup_manager::PopupHolder, swipe_type::{ - SWIPE_TYPE_MODELS, SwipeTypeModel, swipe_type_all_models_downloaded, + SWIPE_TYPE_MODEL, swwipe_type_model_downloaded, swipe_type_delete_all_models, swipe_type_model_path, }, whisper::{ - WHISPER_MODELS, WhisperModel, whisper_any_models_downloaded, whisper_delete_all_models, whisper_model_from_name, + WHISPER_MODELS, whisper_any_models_downloaded, whisper_delete_all_models, whisper_model_from_name, whisper_model_path, }, }, views::{self, ViewUpdateParams}, }; +use crate::util::downloadable_file::DownloadableFile; #[derive(Clone)] enum Task { WhisperDownloadClosed, WhisperRemoveUnused, - WhisperDownload(&'static WhisperModel), + WhisperDownload(&'static DownloadableFile), WhisperDownloadDone, SwipeTypeDownloadClosed, SwipeTypeRemoveAll, @@ -54,8 +56,8 @@ pub struct State { globals: WguiGlobals, tasks: Tasks, parent_tasks: Tasks, - pending_whisper_download: Option<&'static WhisperModel>, - pending_swipe_download: Option<&'static [SwipeTypeModel]>, + pending_whisper_download: Option<&'static DownloadableFile>, + pending_swipe_download: Option<&'static DownloadableFile>, } impl SettingsTab for State { @@ -81,7 +83,7 @@ impl SettingsTab for State { } Task::WhisperDownload(model) => { self.pending_whisper_download = Some(model); - self.show_whisper_download_dialog(model, par.executor.clone()); + self.show_download_dialogue(model, par.executor.clone(), whisper_model_path(model.file_name), Task::WhisperDownloadClosed, Task::WhisperDownloadDone); } Task::WhisperDownloadDone => { if let Some(model) = self.pending_whisper_download.take() { @@ -94,22 +96,22 @@ impl SettingsTab for State { } Task::SwipeTypeDownloadClosed => { self.pending_swipe_download = None; - if !swipe_type_all_models_downloaded().unwrap_or_default() { - par.general_config.swipe_type_models_downloaded = false; + if !swwipe_type_model_downloaded().unwrap_or_default() { + par.general_config.keyboard_swipe_to_type_enabled = false; par.config_change_kind.replace(ConfigChangeKind::Other); self.parent_tasks.push(ParentTask::SetTab(TabNameEnum::Features)); } } Task::SwipeTypeRemoveAll => { let _ = swipe_type_delete_all_models().log_err("could not remove swipe type models"); + par.general_config.keyboard_swipe_to_type_enabled = false; } Task::SwipeTypeDownloadAll => { - self.pending_swipe_download = Some(SWIPE_TYPE_MODELS); - self.show_swipe_type_download_dialog(SWIPE_TYPE_MODELS, par.executor.clone()); + self.pending_swipe_download = Some(&SWIPE_TYPE_MODEL); + self.show_download_dialogue(&SWIPE_TYPE_MODEL, par.executor.clone(), swipe_type_model_path(SWIPE_TYPE_MODEL.file_name), Task::SwipeTypeDownloadClosed, Task::SwipeTypeDownloadDone); } Task::SwipeTypeDownloadDone => { if let Some(_) = self.pending_swipe_download.take() { - par.general_config.swipe_type_models_downloaded = true; par.config_change_kind.replace(ConfigChangeKind::Other); self.parent_tasks.push(ParentTask::SetTab(TabNameEnum::Features)); } @@ -204,7 +206,7 @@ impl State { if par.feats.swipe_to_type { swipe_type_models_button(par.mp, c)?; } - + options_checkbox(par.mp, c, SettingType::KeyboardSwipeToTypeEnabled)?; options_checkbox(par.mp, c, SettingType::NotificationsEnabled)?; options_checkbox(par.mp, c, SettingType::NotificationsSoundEnabled)?; options_checkbox(par.mp, c, SettingType::KeyboardSoundEnabled)?; @@ -240,27 +242,22 @@ impl State { }) } - fn show_whisper_download_dialog(&mut self, model: &WhisperModel, executor: AsyncExecutor) { + fn show_download_dialogue(&mut self, file: &DownloadableFile, executor: AsyncExecutor, target_path: PathBuf, on_closed: Task, on_downloaded: Task) { views::download_file::mount_popup( self.popup_download.clone(), self.frontend_tasks.clone(), - self.tasks.make_callback_box(Task::WhisperDownloadClosed), + self.tasks.make_callback_box(on_closed), views::download_file::Params { globals: self.globals.clone(), executor, - target_path: whisper_model_path(model.file_name), - url: model.url.into(), - on_downloaded: self.tasks.make_callback_box(Task::WhisperDownloadDone), + target_path, + url: file.url.into(), + on_downloaded: self.tasks.make_callback_box(on_downloaded), }, ); } - fn show_swipe_type_download_dialog(&mut self, models: &[SwipeTypeModel], executor: AsyncExecutor) { - //TODO: create views::download_files and use that - unimplemented!() - } - - fn show_whisper_model_dialog_box_download(&mut self, model: &'static WhisperModel) -> anyhow::Result<()> { + fn show_whisper_model_dialog_box_download(&mut self, model: &'static DownloadableFile) -> anyhow::Result<()> { const ACTION_DOWNLOAD: &str = "download"; const ACTION_CANCEL: &str = "cancel"; @@ -464,7 +461,7 @@ fn swipe_type_models_button(mp: &mut MacroParams, parent: WidgetID) -> anyhow::R let id_cell = horiz_cell(mp.layout, parent)?; - let all_downloaded = swipe_type_all_models_downloaded().unwrap_or_default(); + let all_downloaded = swwipe_type_model_downloaded().unwrap_or_default(); let (translation, icon, action) = if all_downloaded { ( "APP_SETTINGS.SWIPE_TYPE.REMOVE_MODELS", @@ -473,7 +470,7 @@ fn swipe_type_models_button(mp: &mut MacroParams, parent: WidgetID) -> anyhow::R ) } else { ( - "APP_SETTINGS.SWIPE_TYPE.DOWNLOAD_MODELS;5.0 MiB", + "APP_SETTINGS.SWIPE_TYPE.DOWNLOAD_MODELS", "dashboard/download.svg", "swipe_type_download", ) diff --git a/dash-frontend/src/util/downloadable_file.rs b/dash-frontend/src/util/downloadable_file.rs new file mode 100644 index 00000000..a23a4dd9 --- /dev/null +++ b/dash-frontend/src/util/downloadable_file.rs @@ -0,0 +1,5 @@ +pub struct DownloadableFile { + pub file_name: &'static str, + pub display_name: &'static str, + pub url: &'static str, +} \ No newline at end of file diff --git a/dash-frontend/src/util/mod.rs b/dash-frontend/src/util/mod.rs index f898e589..a3e6f408 100644 --- a/dash-frontend/src/util/mod.rs +++ b/dash-frontend/src/util/mod.rs @@ -8,3 +8,4 @@ pub mod toast_manager; pub mod wgui_simple; pub mod whisper; pub mod swipe_type; +pub mod downloadable_file; diff --git a/dash-frontend/src/util/swipe_type.rs b/dash-frontend/src/util/swipe_type.rs index e0cfdeb4..faef0db7 100644 --- a/dash-frontend/src/util/swipe_type.rs +++ b/dash-frontend/src/util/swipe_type.rs @@ -1,33 +1,13 @@ use std::{fs, io, path::PathBuf}; use wlx_common::data_dir; +use crate::util::downloadable_file::DownloadableFile; -/// The set of model files required by the swipe-to-type engine. -/// These correspond to the assets that `super-swipe-type` currently -/// downloads via `cached_path` in `SwipeOrchestrator::new()`. -pub struct SwipeTypeModel { - pub file_name: &'static str, - pub url: &'static str, -} - -pub const SWIPE_TYPE_MODELS: &[SwipeTypeModel] = &[ - SwipeTypeModel { - file_name: "swipe_encoder_android.onnx", - url: "https://wayvr.org/files/swipe_type/swipe_encoder_android.onnx", - }, - SwipeTypeModel { - file_name: "swipe_decoder_android.onnx", - url: "https://wayvr.org/files/swipe_type/swipe_decoder_android.onnx", - }, - SwipeTypeModel { - file_name: "en_wordlist.fst", - url: "https://wayvr.org/files/swipe_type/en_wordlist.fst", - }, - SwipeTypeModel { - file_name: "en_bigrams.fst", - url: "https://wayvr.org/files/swipe_type/en_bigrams.fst", - }, -]; +pub const SWIPE_TYPE_MODEL: DownloadableFile = DownloadableFile { + file_name: "en.tar", + display_name: "English Qwerty (15 MiB)", + url: "https://github.com/oneshinyboi/super-swipe-type/raw/refs/tags/v0.4.2/crates/super-swipe-type/assets/en.tar", +}; pub fn swipe_type_model_folder() -> PathBuf { data_dir::get_path("swipe_type") @@ -37,16 +17,13 @@ pub fn swipe_type_model_path(file_name: &str) -> PathBuf { swipe_type_model_folder().join(file_name) } -/// Returns true when every required model file is present on disk. -pub fn swipe_type_all_models_downloaded() -> io::Result { +pub fn swwipe_type_model_downloaded() -> io::Result { let path = swipe_type_model_folder(); if !path.is_dir() { return Ok(false); } - for model in SWIPE_TYPE_MODELS { - if !path.join(model.file_name).exists() { - return Ok(false); - } + if !path.join(SWIPE_TYPE_MODEL.file_name).exists() { + return Ok(false); } Ok(true) } diff --git a/dash-frontend/src/util/whisper.rs b/dash-frontend/src/util/whisper.rs index 9ae5a1f3..ef29fce9 100644 --- a/dash-frontend/src/util/whisper.rs +++ b/dash-frontend/src/util/whisper.rs @@ -1,42 +1,37 @@ use std::{fs, io, path::PathBuf}; use wlx_common::data_dir; +use crate::util::downloadable_file::DownloadableFile; -pub struct WhisperModel { - pub file_name: &'static str, - pub display_name: &'static str, - pub url: &'static str, -} - -pub const WHISPER_MODELS: &[WhisperModel] = &[ - WhisperModel { +pub const WHISPER_MODELS: &[DownloadableFile] = &[ + DownloadableFile { file_name: "ggml-base-q8_0.bin", display_name: "Base Q8 (78MiB)", url: "https://wayvr.org/files/whisper/ggml-base-q8_0.bin", }, - WhisperModel { + DownloadableFile { file_name: "ggml-small-q8_0.bin", display_name: "Small Q8 (252MiB)", url: "https://wayvr.org/files/whisper/ggml-small-q8_0.bin", }, - WhisperModel { + DownloadableFile { file_name: "ggml-large-v3-turbo-q5_0.bin", display_name: "Turbo Q5 (574MiB)", url: "https://wayvr.org/files/whisper/ggml-large-v3-turbo-q5_0.bin", }, - WhisperModel { + DownloadableFile { file_name: "ggml-large-v3-turbo-q8_0.bin", display_name: "Turbo Q8 (874MiB)", url: "https://wayvr.org/files/whisper/ggml-large-v3-turbo-q8_0.bin", }, - WhisperModel { + DownloadableFile { file_name: "ggml-large-v3-turbo.bin", display_name: "Turbo (1.5GiB)", url: "https://wayvr.org/files/whisper/ggml-large-v3-turbo.bin", }, ]; -pub fn whisper_model_from_name(file_name: &str) -> Option<&'static WhisperModel> { +pub fn whisper_model_from_name(file_name: &str) -> Option<&'static DownloadableFile> { WHISPER_MODELS.iter().find(|x| x.file_name == file_name) } diff --git a/wayvr/src/overlays/keyboard/builder.rs b/wayvr/src/overlays/keyboard/builder.rs index 50cb792b..3059e130 100644 --- a/wayvr/src/overlays/keyboard/builder.rs +++ b/wayvr/src/overlays/keyboard/builder.rs @@ -34,7 +34,11 @@ use wgui::{ use wgui::event::StyleSetRequest; use wgui::layout::LayoutTask; use wgui::taffy::Display; -use super::{KeyButtonData, KeyState, KeyboardState, handle_press, handle_release, layout::{self, KeyCapType}, handle_mouse_motion, init_swipe_type_manager}; +#[cfg(feature = "swipe-to-type")] +use wlx_common::data_dir; +use super::{KeyButtonData, KeyState, KeyboardState, handle_press, handle_release, layout::{self, KeyCapType}, handle_mouse_motion}; +#[cfg(feature = "swipe-to-type")] +use super::init_swipe_type_manager; const PIXELS_PER_UNIT: f32 = 60.; @@ -478,7 +482,8 @@ pub(super) fn create_keyboard_panel( } } if app.session.config.keyboard_swipe_to_type_enabled && panel.state.swipe_typing_manager.is_none() { - init_swipe_type_manager(&mut panel.state); + #[cfg(feature = "swipe-to-type")] + init_swipe_type_manager(&mut panel.state, data_dir::get_path("swipe_type")); let predictions_root = panel.parser_state .get_widget_id("swipe_predictions_root") diff --git a/wayvr/src/overlays/keyboard/mod.rs b/wayvr/src/overlays/keyboard/mod.rs index 1ca64d6e..e1c7aecd 100644 --- a/wayvr/src/overlays/keyboard/mod.rs +++ b/wayvr/src/overlays/keyboard/mod.rs @@ -45,6 +45,8 @@ use wlx_common::{ config::AltModifier, overlays::{BackendAttrib, BackendAttribValue}, }; +#[cfg(feature = "swipe-to-type")] +use wlx_common::data_dir; use crate::overlays::keyboard::builder::update_swipe_prediction_bar; use crate::overlays::keyboard::layout::KeyCapType; use crate::overlays::keyboard::swipe_type::SwipeTypingManager; @@ -52,9 +54,32 @@ use crate::overlays::keyboard::swipe_type::SwipeTypingManager; pub mod builder; mod layout; -#[cfg(feature = "swipe-to-type ")] +#[cfg(feature = "swipe-to-type")] mod swipe_type; +#[cfg(not(feature = "swipe-to-type"))] +mod swipe_type { + use std::sync::mpsc::Receiver; + use wgui::event::{DeviceBitmask, MouseButtonIndex}; + use glam::Vec2; + + pub struct SwipeTypingManager; + + impl SwipeTypingManager { + pub fn new(_model_folder: std::path::PathBuf) -> anyhow::Result<(Self, Receiver>>)> { + Ok((Self, std::sync::mpsc::sync_channel(1).1)) + } + pub fn add_swipe(&mut self, _within_key_pos_normalized: &Vec2, _key_label: char, _device: DeviceBitmask, _index: Option) {} + pub fn predict(&mut self) -> anyhow::Result<()> { Ok(()) } + pub fn reset(&mut self) {} + pub fn did_swipe_leave_first_key(&self) -> bool { false } + pub fn is_current_swipe_empty(&self) -> bool { true } + pub fn current_swipe_mouse_button_index(&self) -> Option { None } + pub fn select_word(&mut self, _word: &String, _app: &mut crate::state::AppState, _original_keyboard_mods: crate::subsystem::hid::KeyModifier) {} + pub fn select_alternate_prediction(&mut self, _word: &String, _app: &mut crate::state::AppState, _original_keyboard_mods: crate::subsystem::hid::KeyModifier) {} + } +} + pub const KEYBOARD_NAME: &str = "kbd"; const AUTO_RELEASE_MODS: [KeyModifier; 5] = [SHIFT, CTRL, ALT, SUPER, ALTGR]; const SYSTEM_LAYOUT_ALIASES: [&str; 5] = ["mozc", "pinyin", "hangul", "sayura", "unikey"]; @@ -132,8 +157,9 @@ pub fn create_keyboard(app: &mut AppState, wayland: bool) -> anyhow::Result { state.swipe_typing_manager = Some(engine); state.swipe_candidate_receiver = Some(receiver); @@ -143,6 +169,7 @@ pub(self) fn init_swipe_type_manager(state: &mut KeyboardState) { } }; } +#[cfg(feature = "swipe-to-type")] pub(self) fn hide_swipe_type_manager(panel: &mut GuiPanel) { let predictions_root = panel.parser_state .get_widget_id("swipe_predictions_root") @@ -188,7 +215,8 @@ impl KeyboardBackend { let mut state = self.default_state.take(); if app.session.config.keyboard_swipe_to_type_enabled { - init_swipe_type_manager(&mut state); + #[cfg(feature = "swipe-to-type")] + init_swipe_type_manager(&mut state, data_dir::get_path("swipe_type")); log::info!("swipe engine created"); } @@ -196,6 +224,7 @@ impl KeyboardBackend { create_keyboard_panel(app, keymap, state, &self.wlx_layout)?; if !app.session.config.keyboard_swipe_to_type_enabled { + #[cfg(feature = "swipe-to-type")] hide_swipe_type_manager(&mut panel); } @@ -243,7 +272,8 @@ impl KeyboardBackend { .take(); if app.session.config.keyboard_swipe_to_type_enabled { - init_swipe_type_manager(&mut state_from); + #[cfg(feature = "swipe-to-type")] + init_swipe_type_manager(&mut state_from, data_dir::get_path("swipe_type")); } self.active_layout = new_key; @@ -254,6 +284,7 @@ impl KeyboardBackend { .state = state_from; if !app.session.config.keyboard_swipe_to_type_enabled { + #[cfg(feature = "swipe-to-type")] hide_swipe_type_manager(self.layout_panels .get_mut(self.active_layout) .unwrap() diff --git a/wayvr/src/overlays/keyboard/swipe_type.rs b/wayvr/src/overlays/keyboard/swipe_type.rs index f2f67970..a943d358 100644 --- a/wayvr/src/overlays/keyboard/swipe_type.rs +++ b/wayvr/src/overlays/keyboard/swipe_type.rs @@ -3,6 +3,7 @@ use crate::subsystem::hid::{KeyModifier, VirtualKey, CTRL}; use anyhow::{bail}; use glam::Vec2; use std::mem; +use std::path::PathBuf; use std::sync::mpsc::{sync_channel, Receiver, SyncSender, channel, Sender}; use std::thread::{self, JoinHandle}; use std::time::Instant; @@ -87,14 +88,14 @@ impl SwipeTypingManager { app.hid_provider .set_modifiers_routed(app.wvr_server.as_mut(), original_keyboard_mods); } - pub fn new() -> anyhow::Result<(SwipeTypingManager, Receiver>>)> { + pub fn new(model_folder: PathBuf) -> anyhow::Result<(SwipeTypingManager, Receiver>>)> { let (candidate_sender, candidate_receiver) = sync_channel(1); let (task_sender, task_receiver) = channel::(); // Spawn persistent worker thread let worker_candidate_sender = candidate_sender.clone(); let worker_thread = thread::spawn(move || { - let mut swipe_engine = match SwipeOrchestrator::new() { + let mut swipe_engine = match SwipeOrchestrator::new_with_paths(&model_folder) { Ok(engine) => engine, Err(e) => { log::error!("Failed to initialize SwipeOrchestrator: {}", e);