From 1fa8b6229a1a54d71e921c01a7d427c97f03d7ad Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Wed, 22 Sep 2021 14:13:24 -0700 Subject: [PATCH 1/3] fix(backend): Do not print error when EC isn't found --- backend/src/daemon/server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/daemon/server.rs b/backend/src/daemon/server.rs index 50f6e09d..5cd82c2c 100644 --- a/backend/src/daemon/server.rs +++ b/backend/src/daemon/server.rs @@ -50,7 +50,7 @@ impl DaemonServer { } }, Err(err) => { - error!("Failed to access LPC EC: {:?}", err); + info!("No LPC EC found: {:?}", err); } } From 559117e99a529a33a19f65d622f41f75dcc2cdfd Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 27 Sep 2021 13:35:17 -0700 Subject: [PATCH 2/3] improv: Move logic from `daemon()` func into `backend` crate --- backend/src/backend.rs | 18 +++++++++++++++++- src/main_window.rs | 19 +------------------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/backend/src/backend.rs b/backend/src/backend.rs index f68f522f..fd8c2f64 100644 --- a/backend/src/backend.rs +++ b/backend/src/backend.rs @@ -99,10 +99,26 @@ impl Backend { Self::new_internal(DaemonClient::new_pkexec()) } - pub fn new() -> Result { + pub fn new_stdio() -> Result { Self::new_internal(DaemonServer::new_stdio()?) } + #[cfg(target_os = "linux")] + pub fn new() -> Result { + if unsafe { libc::geteuid() == 0 } { + info!("Already running as root"); + Self::new_stdio() + } else { + info!("Not running as root, spawning daemon with pkexec"); + Self::new_pkexec() + } + } + + #[cfg(not(target_os = "linux"))] + pub fn new() -> Result { + Self::new_stdio().expect("Failed to create server") + } + fn inner(&self) -> &BackendInner { BackendInner::from_instance(self) } diff --git a/src/main_window.rs b/src/main_window.rs index 18363e8e..59d4e731 100644 --- a/src/main_window.rs +++ b/src/main_window.rs @@ -211,7 +211,7 @@ impl MainWindow { app.add_window(&window); let backend = cascade! { - daemon(); + Backend::new().expect("Failed to create server"); ..connect_board_loading(clone!(@weak window => move || { let loader = window.display_loader(&fl!("loading")); *window.inner().board_loading.borrow_mut() = Some(loader); @@ -398,20 +398,3 @@ impl MainWindow { Loader(self.clone(), load_hbox) } } - -#[cfg(target_os = "linux")] -fn daemon() -> Backend { - if unsafe { libc::geteuid() == 0 } { - info!("Already running as root"); - Backend::new() - } else { - info!("Not running as root, spawning daemon with pkexec"); - Backend::new_pkexec() - } - .expect("Failed to create server") -} - -#[cfg(not(target_os = "linux"))] -fn daemon() -> Backend { - Backend::new().expect("Failed to create server") -} From eee7a310778424106700e07557da3ec79e264120 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 21 Sep 2021 16:05:37 -0700 Subject: [PATCH 3/3] feat: Initial command line Implements https://github.com/pop-os/keyboard-configurator/issues/84. Uses clap derive for argument parsing. Could be improved, since resetting layout (for instance) unnecessarily involves the backend fetching the layout for all connected keyboard. Printing info messages by default seems good with the GUI, a bit more out of place with CLI commands. --- .github/workflows/ci.yml | 2 +- Cargo.lock | 353 +++++++----------- Cargo.toml | 2 + backend/src/board.rs | 74 +++- backend/src/daemon/daemon_thread.rs | 8 +- backend/src/layout/meta.rs | 2 + i18n/en/system76_keyboard_configurator.ftl | 6 - ...system76_keyboard_configurator_backend.ftl | 10 +- layouts/system76/launch_1/meta.json | 1 + layouts/system76/launch_alpha_1/meta.json | 3 +- layouts/system76/launch_alpha_2/meta.json | 3 +- src/cli.rs | 130 +++++++ src/configurator_app.rs | 166 +++++--- src/keyboard.rs | 76 +--- src/main.rs | 1 + src/main_window.rs | 2 +- 16 files changed, 479 insertions(+), 360 deletions(-) create mode 100644 src/cli.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f9cefea6..1b43aeec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,7 +77,7 @@ jobs: with: name: keyboard-configurator-mingw32-${{ github.sha }} - run: msiexec /i keyboard-configurator.msi /qb - - run: '& "C:\Program Files (x86)\System76\Keyboard Configurator\system76-keyboard-configurator.exe" --help-gtk' + - run: '& "C:\Program Files (x86)\System76\Keyboard Configurator\system76-keyboard-configurator.exe" --help' macos: runs-on: macos-latest diff --git a/Cargo.lock b/Cargo.lock index 5ad21993..6209aa5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,7 +62,7 @@ checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" dependencies = [ "atk-sys", "bitflags", - "glib 0.15.2", + "glib", "libc", ] @@ -72,10 +72,10 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58aeb089fb698e06db8089971c7ee317ab9644bade33383f63631437b03aafb6" dependencies = [ - "glib-sys 0.15.1", - "gobject-sys 0.15.1", + "glib-sys", + "gobject-sys", "libc", - "system-deps 6.0.0", + "system-deps", ] [[package]] @@ -119,19 +119,6 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" -[[package]] -name = "cairo-rs" -version = "0.14.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b5725979db0c586d98abad2193cdb612dd40ef95cd26bd99851bf93b3cb482" -dependencies = [ - "bitflags", - "cairo-sys-rs 0.14.9", - "glib 0.14.8", - "libc", - "thiserror", -] - [[package]] name = "cairo-rs" version = "0.15.1" @@ -139,32 +126,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b869e97a87170f96762f9f178eae8c461147e722ba21dd8814105bf5716bf14a" dependencies = [ "bitflags", - "cairo-sys-rs 0.15.1", - "glib 0.15.2", + "cairo-sys-rs", + "glib", "libc", "thiserror", ] -[[package]] -name = "cairo-sys-rs" -version = "0.14.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b448b876970834fda82ba3aeaccadbd760206b75388fc5c1b02f1e343b697570" -dependencies = [ - "glib-sys 0.14.0", - "libc", - "system-deps 3.2.0", -] - [[package]] name = "cairo-sys-rs" version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8" dependencies = [ - "glib-sys 0.15.1", + "glib-sys", "libc", - "system-deps 6.0.0", + "system-deps", ] [[package]] @@ -179,15 +155,6 @@ version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" -[[package]] -name = "cfg-expr" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b412e83326147c2bb881f8b40edfbf9905b9b8abaebd0e47ca190ba62fda8f0e" -dependencies = [ - "smallvec", -] - [[package]] name = "cfg-expr" version = "0.9.0" @@ -219,11 +186,41 @@ dependencies = [ "atty", "bitflags", "strsim 0.8.0", - "textwrap", + "textwrap 0.11.0", "unicode-width", "vec_map", ] +[[package]] +name = "clap" +version = "3.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced1892c55c910c1219e98d6fc8d71f6bddba7905866ce740066d8bfea859312" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "indexmap", + "lazy_static", + "os_str_bytes", + "strsim 0.10.0", + "termcolor", + "textwrap 0.15.0", +] + +[[package]] +name = "clap_derive" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da95d038ede1a964ce99f49cbe27a7fb538d1da595e4b4f70b8c8f338d17bf16" +dependencies = [ + "heck 0.4.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "concurrent-queue" version = "1.2.2" @@ -260,12 +257,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - [[package]] name = "enumflags2" version = "0.6.4" @@ -502,11 +493,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "614258e81ec35ed8770e64a0838f3a47f95b398bc51e724d3b3fa09c1ee0f8d5" dependencies = [ "bitflags", - "cairo-rs 0.15.1", + "cairo-rs", "gdk-pixbuf", "gdk-sys", - "gio 0.15.2", - "glib 0.15.2", + "gio", + "glib", "libc", "pango", ] @@ -519,8 +510,8 @@ checksum = "172dfe1d9dfb62936bf7ad3ede2913a1b21b1e3db56990e46e00789201de9070" dependencies = [ "bitflags", "gdk-pixbuf-sys", - "gio 0.15.2", - "glib 0.15.2", + "gio", + "glib", "libc", ] @@ -530,11 +521,11 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413424d9818621fa3cfc8a3a915cdb89a7c3c507d56761b4ec83a9a98e587171" dependencies = [ - "gio-sys 0.15.1", - "glib-sys 0.15.1", - "gobject-sys 0.15.1", + "gio-sys", + "glib-sys", + "gobject-sys", "libc", - "system-deps 6.0.0", + "system-deps", ] [[package]] @@ -543,15 +534,15 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32e7a08c1e8f06f4177fb7e51a777b8c1689f743a7bc11ea91d44d2226073a88" dependencies = [ - "cairo-sys-rs 0.15.1", + "cairo-sys-rs", "gdk-pixbuf-sys", - "gio-sys 0.15.1", - "glib-sys 0.15.1", - "gobject-sys 0.15.1", + "gio-sys", + "glib-sys", + "gobject-sys", "libc", "pango-sys", "pkg-config", - "system-deps 6.0.0", + "system-deps", ] [[package]] @@ -576,23 +567,6 @@ dependencies = [ "wasi 0.10.2+wasi-snapshot-preview1", ] -[[package]] -name = "gio" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711c3632b3ebd095578a9c091418d10fed492da9443f58ebc8f45efbeb215cb0" -dependencies = [ - "bitflags", - "futures-channel", - "futures-core", - "futures-io", - "gio-sys 0.14.0", - "glib 0.14.8", - "libc", - "once_cell", - "thiserror", -] - [[package]] name = "gio" version = "0.15.2" @@ -603,58 +577,26 @@ dependencies = [ "futures-channel", "futures-core", "futures-io", - "gio-sys 0.15.1", - "glib 0.15.2", + "gio-sys", + "glib", "libc", "once_cell", "thiserror", ] -[[package]] -name = "gio-sys" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0a41df66e57fcc287c4bcf74fc26b884f31901ea9792ec75607289b456f48fa" -dependencies = [ - "glib-sys 0.14.0", - "gobject-sys 0.14.0", - "libc", - "system-deps 3.2.0", - "winapi", -] - [[package]] name = "gio-sys" version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04b57719ccaacf2a0d9c79f151be629f3a3ef3991658ee2af0bb66287e4ea86c" dependencies = [ - "glib-sys 0.15.1", - "gobject-sys 0.15.1", + "glib-sys", + "gobject-sys", "libc", - "system-deps 6.0.0", + "system-deps", "winapi", ] -[[package]] -name = "glib" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c515f1e62bf151ef6635f528d05b02c11506de986e43b34a5c920ef0b3796a4" -dependencies = [ - "bitflags", - "futures-channel", - "futures-core", - "futures-executor", - "futures-task", - "glib-macros 0.14.1", - "glib-sys 0.14.0", - "gobject-sys 0.14.0", - "libc", - "once_cell", - "smallvec", -] - [[package]] name = "glib" version = "0.15.2" @@ -666,30 +608,15 @@ dependencies = [ "futures-core", "futures-executor", "futures-task", - "glib-macros 0.15.1", - "glib-sys 0.15.1", - "gobject-sys 0.15.1", + "glib-macros", + "glib-sys", + "gobject-sys", "libc", "once_cell", "smallvec", "thiserror", ] -[[package]] -name = "glib-macros" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aad66361f66796bfc73f530c51ef123970eb895ffba991a234fcf7bea89e518" -dependencies = [ - "anyhow", - "heck 0.3.3", - "proc-macro-crate 1.1.0", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "glib-macros" version = "0.15.1" @@ -705,16 +632,6 @@ dependencies = [ "syn", ] -[[package]] -name = "glib-sys" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c1d60554a212445e2a858e42a0e48cece1bd57b311a19a9468f70376cf554ae" -dependencies = [ - "libc", - "system-deps 3.2.0", -] - [[package]] name = "glib-sys" version = "0.15.1" @@ -722,18 +639,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c668102c6e15e0a7f6b99b59f602c2e806967bb86414f617b77e19b1de5b3fac" dependencies = [ "libc", - "system-deps 6.0.0", -] - -[[package]] -name = "gobject-sys" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa92cae29759dae34ab5921d73fff5ad54b3d794ab842c117e36cafc7994c3f5" -dependencies = [ - "glib-sys 0.14.0", - "libc", - "system-deps 3.2.0", + "system-deps", ] [[package]] @@ -742,9 +648,9 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6edb1f0b3e4c08e2a0a490d1082ba9e902cdff8ff07091e85c6caec60d17e2ab" dependencies = [ - "glib-sys 0.15.1", + "glib-sys", "libc", - "system-deps 6.0.0", + "system-deps", ] [[package]] @@ -755,13 +661,13 @@ checksum = "22fa27087b0773dbf10d4d434fc38261f2df9a606571462fc13e61bea7bf2b8e" dependencies = [ "atk", "bitflags", - "cairo-rs 0.15.1", + "cairo-rs", "field-offset", "futures-channel", "gdk", "gdk-pixbuf", - "gio 0.15.2", - "glib 0.15.2", + "gio", + "glib", "gtk-sys", "gtk3-macros", "libc", @@ -777,15 +683,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "013419d486809ebafd7d8c2450afcfcf384c1a52dd079660f88c2a3b2e19f82f" dependencies = [ "atk-sys", - "cairo-sys-rs 0.15.1", + "cairo-sys-rs", "gdk-pixbuf-sys", "gdk-sys", - "gio-sys 0.15.1", - "glib-sys 0.15.1", - "gobject-sys 0.15.1", + "gio-sys", + "glib-sys", + "gobject-sys", "libc", "pango-sys", - "system-deps 6.0.0", + "system-deps", ] [[package]] @@ -802,6 +708,12 @@ dependencies = [ "syn", ] +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + [[package]] name = "heck" version = "0.3.3" @@ -912,6 +824,16 @@ dependencies = [ "syn", ] +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "instant" version = "0.1.12" @@ -941,15 +863,6 @@ dependencies = [ "unic-langid", ] -[[package]] -name = "itertools" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "0.4.8" @@ -1110,6 +1023,15 @@ dependencies = [ "serde", ] +[[package]] +name = "os_str_bytes" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" +dependencies = [ + "memchr", +] + [[package]] name = "palette" version = "0.5.0" @@ -1141,7 +1063,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79211eff430c29cc38c69e0ab54bc78fa1568121ca9737707eee7f92a8417a94" dependencies = [ "bitflags", - "glib 0.15.2", + "glib", "libc", "once_cell", "pango-sys", @@ -1153,10 +1075,10 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7022c2fb88cd2d9d55e1a708a8c53a3ae8678234c4a54bf623400aeb7f31fac2" dependencies = [ - "glib-sys 0.15.1", - "gobject-sys 0.15.1", + "glib-sys", + "gobject-sys", "libc", - "system-deps 6.0.0", + "system-deps", ] [[package]] @@ -1166,8 +1088,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7876a45c1f1d1a75a2601dc6d9ef2cb5a8be0e3d76f909d82450759929035366" dependencies = [ "bitflags", - "cairo-rs 0.15.1", - "glib 0.15.2", + "cairo-rs", + "glib", "libc", "pango", "pangocairo-sys", @@ -1179,11 +1101,11 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78cf746594916c81d5f739af9335c5f55a1f4606d80b3e1d821f18cf95a29494" dependencies = [ - "cairo-sys-rs 0.15.1", - "glib-sys 0.15.1", + "cairo-sys-rs", + "glib-sys", "libc", "pango-sys", - "system-deps 6.0.0", + "system-deps", ] [[package]] @@ -1639,18 +1561,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] -name = "strum" -version = "0.21.0" +name = "structopt" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2" +checksum = "bf9d950ef167e25e0bdb073cf1d68e9ad2795ac826f2f3f59647817cf23c0bfa" +dependencies = [ + "clap 2.33.3", + "lazy_static", + "structopt-derive", +] [[package]] -name = "strum_macros" -version = "0.21.1" +name = "structopt-derive" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" +checksum = "134d838a2c9943ac3125cf6df165eda53493451b719f3255b2a26b85f772d0ba" dependencies = [ "heck 0.3.3", + "proc-macro-error", "proc-macro2", "quote", "syn", @@ -1667,46 +1595,28 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "system-deps" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "480c269f870722b3b08d2f13053ce0c2ab722839f472863c3e2d61ff3a1c2fa6" -dependencies = [ - "anyhow", - "cfg-expr 0.8.1", - "heck 0.3.3", - "itertools", - "pkg-config", - "strum", - "strum_macros", - "thiserror", - "toml", - "version-compare 0.0.11", -] - [[package]] name = "system-deps" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b1487aaddaacbc5d60a2a507ba1617c5ca66c57dd0dd07d0c5efd5b693841d4" dependencies = [ - "cfg-expr 0.9.0", + "cfg-expr", "heck 0.3.3", "pkg-config", "toml", - "version-compare 0.1.0", + "version-compare", ] [[package]] name = "system76-keyboard-configurator" version = "1.1.0" dependencies = [ - "cairo-rs 0.14.9", "cascade", + "clap 3.1.5", "env_logger", "futures", - "gio 0.14.8", + "gio", "gtk", "i18n-embed", "i18n-embed-fl", @@ -1717,6 +1627,7 @@ dependencies = [ "rust-embed", "serde", "serde_json", + "structopt", "system76-keyboard-configurator-backend", "system76-keyboard-configurator-widgets", "winreg", @@ -1729,7 +1640,7 @@ dependencies = [ "cascade", "futures", "futures-timer", - "glib 0.15.2", + "glib", "hidapi", "i18n-embed", "i18n-embed-fl", @@ -1753,7 +1664,7 @@ version = "0.1.0" dependencies = [ "cascade", "futures", - "gio 0.15.2", + "gio", "gtk", "i18n-embed", "i18n-embed-fl", @@ -1773,7 +1684,7 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fa1f8f8e61d62f04c704268e040df5f37bd454af161c147bb2b491f68661ebe" dependencies = [ - "clap", + "clap 2.33.3", "downcast-rs", "hidapi", "libc", @@ -1797,6 +1708,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "textwrap" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" + [[package]] name = "thiserror" version = "1.0.30" @@ -1903,12 +1820,6 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" -[[package]] -name = "version-compare" -version = "0.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" - [[package]] name = "version-compare" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 5f13be14..28156631 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ "tools", "ffi", "backend", "widgets" ] [dependencies] cascade = "1" +clap = { version = "3", features = ["derive"] } futures = "0.3.13" gtk = { version = "0.15.0", features = ["v3_22"] } libc = "0.2" @@ -17,6 +18,7 @@ once_cell = "1.4" pangocairo = "0.15.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +structopt = "0.3" log = "0.4.0" env_logger = "0.8.3" backend = { package = "system76-keyboard-configurator-backend", path = "backend" } diff --git a/backend/src/board.rs b/backend/src/board.rs index 18d9f632..0bf6d3c3 100644 --- a/backend/src/board.rs +++ b/backend/src/board.rs @@ -1,4 +1,4 @@ -use futures::{channel::mpsc as async_mpsc, prelude::*}; +use futures::{channel::mpsc as async_mpsc, prelude::*, stream::FuturesUnordered}; use glib::{ clone, prelude::*, @@ -9,13 +9,14 @@ use once_cell::sync::Lazy; use std::{ cell::{Cell, Ref, RefCell}, collections::HashMap, + pin::Pin, sync::Arc, }; use crate::daemon::ThreadClient; use crate::{ - Benchmark, BoardId, Daemon, DerefCell, Key, KeyMap, KeyMapLayer, Layer, Layout, Matrix, Nelson, - NelsonKind, + fl, Benchmark, BoardId, Daemon, DerefCell, Key, KeyMap, KeyMapLayer, Layer, Layout, Matrix, + Mode, Nelson, NelsonKind, }; #[derive(Default)] @@ -179,6 +180,16 @@ impl Board { &self.inner().model } + pub fn display_name(&self) -> String { + let name = &self.layout().meta.display_name; + let model = self.model().splitn(2, '/').nth(1).unwrap(); + if self.is_fake() { + format!("{} ({})", name, fl!("board-fake", model = model)) + } else { + format!("{} ({})", name, model) + } + } + pub fn version(&self) -> &str { &self.inner().version } @@ -254,6 +265,63 @@ impl Board { self.inner().matrix.borrow() } + pub async fn import_keymap(&self, keymap: KeyMap) { + // TODO: Ideally don't want this function to be O(Keys^2) + // TODO: Make sure it doesn't panic with invalid json with invalid indexes? + + let key_indices = self + .keys() + .iter() + .enumerate() + .map(|(i, k)| (&k.logical_name, i)) + .collect::>(); + + let futures = FuturesUnordered::>>>::new(); + + for (k, v) in &keymap.map { + for (layer, scancode_name) in v.iter().enumerate() { + let n = key_indices[&k]; + futures.push(Box::pin(async move { + if let Err(err) = self.keys()[n].set_scancode(layer, scancode_name).await { + error!("{}: {:?}", fl!("error-set-keymap"), err); + } + })); + } + } + + for (k, hs) in &keymap.key_leds { + let res = self.keys()[key_indices[&k]].set_color(*hs); + futures.push(Box::pin(async move { + if let Err(err) = res.await { + error!("{}: {}", fl!("error-key-led"), err); + } + })); + } + + for (i, keymap_layer) in keymap.layers.iter().enumerate() { + let layer = &self.layers()[i]; + if let Some((mode, speed)) = keymap_layer.mode { + futures.push(Box::pin(async move { + if let Err(err) = layer.set_mode(Mode::from_index(mode).unwrap(), speed).await { + error!("{}: {}", fl!("error-set-layer-mode"), err) + } + })); + } + futures.push(Box::pin(async move { + if let Err(err) = layer.set_brightness(keymap_layer.brightness).await { + error!("{}: {}", fl!("error-set-layer-brightness"), err) + } + })); + futures.push(Box::pin(async move { + if let Err(err) = layer.set_color(keymap_layer.color).await { + error!("{}: {}", fl!("error-set-layer-color"), err) + } + })); + } + + futures.collect::<()>().await; + } + pub fn export_keymap(&self) -> KeyMap { let mut map = HashMap::new(); let mut key_leds = HashMap::new(); diff --git a/backend/src/daemon/daemon_thread.rs b/backend/src/daemon/daemon_thread.rs index 4904493e..d623c9a2 100644 --- a/backend/src/daemon/daemon_thread.rs +++ b/backend/src/daemon/daemon_thread.rs @@ -442,11 +442,9 @@ impl Thread { } } - if have_new_board { - let _ = self - .response_channel - .unbounded_send(ThreadResponse::BoardLoadingDone); - } + let _ = self + .response_channel + .unbounded_send(ThreadResponse::BoardLoadingDone); Ok(()) } diff --git a/backend/src/layout/meta.rs b/backend/src/layout/meta.rs index c62bd6fc..fef50eee 100644 --- a/backend/src/layout/meta.rs +++ b/backend/src/layout/meta.rs @@ -30,4 +30,6 @@ pub struct Meta { #[serde(default = "num_layers_default")] pub num_layers: u8, pub pressed_color: Rgb, + #[serde(default)] + pub is_usb: bool, } diff --git a/i18n/en/system76_keyboard_configurator.ftl b/i18n/en/system76_keyboard_configurator.ftl index a3581c8e..8c12514b 100644 --- a/i18n/en/system76_keyboard_configurator.ftl +++ b/i18n/en/system76_keyboard_configurator.ftl @@ -3,8 +3,6 @@ app-about = About {-name} app-title = System76 {-name} -board-fake = {$model}, fake - button-cancel = Cancel button-configure = Configure Keyboard button-disable = Disable @@ -16,15 +14,11 @@ button-stop = Stop error-disable-key = Failed to disable key error-export-keymap = Failed to export keymap error-import-keymap = Failed to import keymap -error-key-led = Failed to key LED error-open-file = Failed to open file error-save-leds = Failed to save LEDs error-set-keyboard-brightness = Error setting brightness error-set-keyboard-mode = Error setting keyboard mode error-set-keymap = Failed to set keymap -error-set-layer-brightness = Failed to set layer brightness -error-set-layer-color = Failed to set layer color -error-set-layer-mode = Failed to set layer mode error-unsupported-keymap = Unsupported keymap file error-unsupported-keymap-desc = Keymap file appears to be from newer Configurator version. diff --git a/i18n/en/system76_keyboard_configurator_backend.ftl b/i18n/en/system76_keyboard_configurator_backend.ftl index 1bf09f79..0581391c 100644 --- a/i18n/en/system76_keyboard_configurator_backend.ftl +++ b/i18n/en/system76_keyboard_configurator_backend.ftl @@ -1,3 +1,9 @@ +error-key-led = Failed to key LED +error-set-keymap = Failed to set keymap +error-set-layer-brightness = Failed to set layer brightness +error-set-layer-color = Failed to set layer color +error-set-layer-mode = Failed to set layer mode + mode-disabled = Disabled mode-solid-color = Per Layer Solid Color mode-per-key = Per Key Solid @@ -14,4 +20,6 @@ mode-raindrops = Elements mode-splash = Splashdown mode-multisplash = Meteor Shower -no-board = No board \ No newline at end of file +no-board = No board + +board-fake = {$model}, fake diff --git a/layouts/system76/launch_1/meta.json b/layouts/system76/launch_1/meta.json index 7a70c50c..d36814c7 100644 --- a/layouts/system76/launch_1/meta.json +++ b/layouts/system76/launch_1/meta.json @@ -1,5 +1,6 @@ { "display_name": "Launch Keyboard", + "is_usb": true, "has_mode": true, "has_per_layer": true, "num_layers": 4, diff --git a/layouts/system76/launch_alpha_1/meta.json b/layouts/system76/launch_alpha_1/meta.json index 689b78da..c204d9c3 100644 --- a/layouts/system76/launch_alpha_1/meta.json +++ b/layouts/system76/launch_alpha_1/meta.json @@ -1,7 +1,8 @@ { "display_name": "Launch Alpha Keyboard", + "is_usb": true, "has_brightness": true, "has_color": true, "pressed_color": "#202020", "keyboard": "system76/launch_alpha_1" -} \ No newline at end of file +} diff --git a/layouts/system76/launch_alpha_2/meta.json b/layouts/system76/launch_alpha_2/meta.json index 1817a72f..f7451e14 100644 --- a/layouts/system76/launch_alpha_2/meta.json +++ b/layouts/system76/launch_alpha_2/meta.json @@ -1,7 +1,8 @@ { "display_name": "Launch Alpha Keyboard", + "is_usb": true, "has_brightness": true, "has_color": true, "pressed_color": "#202020", "keyboard": "system76/launch_alpha_2" -} \ No newline at end of file +} diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 00000000..2b1eb1a2 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,130 @@ +use backend::{Backend, Board, KeyMap}; +use futures::channel::oneshot; +use gtk::{glib::clone, prelude::*}; +use std::{ + cell::RefCell, + fs::File, + io::{self, Write}, + process, + rc::Rc, +}; + +pub enum Device { + Any, + Internal, + Usb, +} + +impl Device { + pub fn new(internal: bool, usb: bool) -> Self { + if internal { + Self::Internal + } else if usb { + Self::Usb + } else { + Self::Any + } + } +} + +async fn backend_boards() -> (Backend, Vec) { + let backend = Backend::new().expect("Failed to create server"); + + let boards = Rc::new(RefCell::new(Vec::new())); + let id1 = backend.connect_board_added(clone!(@strong boards => move |board| { + boards.borrow_mut().push(board.clone()); + })); + + let (sender, receiver) = oneshot::channel::<()>(); + let sender = RefCell::new(Some(sender)); + let id2 = backend.connect_board_loading_done(move || { + if let Some(sender) = sender.borrow_mut().take() { + sender.send(()).unwrap(); + } + }); + backend.refresh(); + receiver.await.unwrap(); + + backend.disconnect(id1); + backend.disconnect(id2); + + (backend, boards.take()) +} + +pub async fn list_boards() { + let (_backend, boards) = backend_boards().await; + + for board in boards { + println!("{}", board.display_name()); + } +} + +fn match_board(board: &Board, device: &Device) -> bool { + let is_usb = board.layout().meta.is_usb; + match device { + Device::Any => true, + Device::Internal => !is_usb, + Device::Usb => is_usb, + } +} + +pub async fn board(device: Device) -> Board { + let (backend, mut boards) = backend_boards().await; + + boards = boards + .into_iter() + .filter(|board| match_board(board, &device)) + .collect(); + + if boards.is_empty() { + error!("No board detected"); + process::exit(1) + } else if boards.len() == 1 { + boards[0].clone() + } else { + eprintln!("Multiple boards detected"); + for (i, board) in boards.iter().enumerate() { + // Human readable name? + eprintln!("[{}] {}", i, board.display_name()); + } + print!("> "); + io::stdout().lock().flush().unwrap(); + let mut selection = String::new(); + io::stdin().read_line(&mut selection).unwrap(); + // XXX panic + boards + .get(selection.trim().parse::().unwrap()) + .unwrap() + .clone() + } +} + +// usb: bool +pub async fn save(device: Device, path: String) { + let board = board(device).await; + let keymap = board.export_keymap(); + match File::create(&path) { + Ok(file) => match keymap.to_writer_pretty(file) { + Ok(()) => (), + Err(err) => todo!(), + }, + Err(err) => todo!(), + } +} + +pub async fn load(device: Device, path: String) { + let board = board(device).await; + let keymap = match File::open(&path) { + Ok(file) => match KeyMap::from_reader(file) { + Ok(keymap) => keymap, + Err(err) => todo!(), + }, + Err(err) => todo!(), + }; + board.import_keymap(keymap).await; +} + +pub async fn reset(device: Device) { + let board = board(device).await; + board.import_keymap(board.layout().default.clone()).await; +} diff --git a/src/configurator_app.rs b/src/configurator_app.rs index bfabed31..c1a285e7 100644 --- a/src/configurator_app.rs +++ b/src/configurator_app.rs @@ -1,15 +1,71 @@ use cascade::cascade; -use gtk::{gdk, gio, glib, prelude::*, subclass::prelude::*}; -use std::cell::Cell; - -use crate::{about_dialog, fl, MainWindow, Page}; +use clap::Parser; +use gtk::{ + gdk, + gio::{self, subclass::ArgumentList}, + glib::{self, clone}, + prelude::*, + subclass::prelude::*, +}; +use std::{future::Future, pin::Pin}; + +use crate::{about_dialog, cli, fl, MainWindow, Page}; use backend::DerefCell; +#[derive(Debug, Parser)] +enum Verb { + #[clap(about = "Print list of detected keyboards")] + ListBoards, + #[clap(about = "Save layout to JSON")] + Save { + path: String, + #[clap(long)] + internal: bool, + #[clap(long, conflicts_with = "internal")] + usb: bool, + }, + #[clap(about = "Load layout from JSON")] + Load { + path: String, + #[clap(long)] + internal: bool, + #[clap(long, conflicts_with = "internal")] + usb: bool, + }, + #[clap(about = "Reset layout to default")] + Reset { + #[clap(long)] + internal: bool, + #[clap(long, conflicts_with = "internal")] + usb: bool, + }, +} + +#[derive(Debug, Parser)] +struct Opt { + #[clap(subcommand)] + verb: Option, + #[clap( + short = 'k', + long, + use_delimiter = true, + help_heading = "DEBUGGING OPTIONS", + help = "Keyboards to emulate, or 'all'" + )] + fake_keyboard: Vec, + #[clap(long, help_heading = "DEBUGGING OPTIONS", help = "Show debug layers")] + debug_layers: bool, + #[clap( + long, + help_heading = "DEBUGGING OPTIONS", + help = "Show Launch testing section" + )] + launch_test: bool, +} + #[derive(Default)] pub struct ConfiguratorAppInner { - phony_board_names: DerefCell>, - debug_layers: Cell, - launch_test: Cell, + opt: DerefCell, } #[glib::object_subclass] @@ -22,53 +78,63 @@ impl ObjectSubclass for ConfiguratorAppInner { impl ObjectImpl for ConfiguratorAppInner { fn constructed(&self, app: &ConfiguratorApp) { app.set_application_id(Some("com.system76.keyboardconfigurator")); - self.parent_constructed(app); - - app.add_main_option( - "fake-keyboard", - glib::Char::from(b'k'), - glib::OptionFlags::NONE, - glib::OptionArg::String, - "", - None, - ); - app.add_main_option( - "debug-layers", - glib::Char::from(b'\0'), - glib::OptionFlags::NONE, - glib::OptionArg::None, - "", - None, - ); - app.add_main_option( - "launch-test", - glib::Char::from(b'\0'), - glib::OptionFlags::NONE, - glib::OptionArg::None, - "", - None, - ); } } impl ApplicationImpl for ConfiguratorAppInner { - fn handle_local_options(&self, _app: &ConfiguratorApp, opts: &glib::VariantDict) -> i32 { - fn lookup(opts: &glib::VariantDict, key: &str) -> Option { - opts.lookup_value(key, None)?.get() + fn local_command_line(&self, app: &ConfiguratorApp, args: &mut ArgumentList) -> Option { + match Opt::try_parse_from(args.iter()) { + Ok(mut opt) => { + if opt.fake_keyboard == ["all".to_string()] { + opt.fake_keyboard = backend::layouts().iter().map(|s| s.to_string()).collect(); + } + + self.opt.set(opt); + + let fut: Pin>> = if let Some(verb) = &self.opt.verb { + match verb { + Verb::ListBoards => Box::pin(cli::list_boards()), + Verb::Save { + path, + internal, + usb, + } => Box::pin(cli::save(cli::Device::new(*internal, *usb), path.clone())), + Verb::Load { + path, + internal, + usb, + } => Box::pin(cli::load(cli::Device::new(*internal, *usb), path.clone())), + Verb::Reset { internal, usb } => { + Box::pin(cli::reset(cli::Device::new(*internal, *usb))) + } + } + } else { + app.register(None::<&gio::Cancellable>).unwrap(); + app.activate(); + return Some(0); + }; + + let main_loop = glib::MainLoop::new(None, false); + glib::MainContext::default().spawn_local(clone!(@strong main_loop => async move { + fut.await; + main_loop.quit(); + })); + main_loop.run(); + } + Err(err) => { + if err.kind() == clap::ErrorKind::DisplayHelp { + eprintln!("{}", err); + } else if err.kind() == clap::ErrorKind::DisplayVersion { + eprintln!("{}", err); + } else { + eprintln!("Error parsing arguments: {}", err); + return Some(1); + } + } } - let board_names = match lookup::(opts, "fake-keyboard").as_deref() { - Some("all") => backend::layouts().iter().map(|s| s.to_string()).collect(), - Some(value) => value.split(',').map(str::to_string).collect(), - None => vec![], - }; - - self.phony_board_names.set(board_names); - self.debug_layers.set(opts.contains("debug-layers")); - self.launch_test.set(opts.contains("launch-test")); - - -1 + Some(0) } fn startup(&self, app: &ConfiguratorApp) { @@ -117,15 +183,15 @@ impl ConfiguratorApp { } pub fn phony_board_names(&self) -> &[String] { - &self.inner().phony_board_names + &self.inner().opt.fake_keyboard } pub fn debug_layers(&self) -> bool { - self.inner().debug_layers.get() + self.inner().opt.debug_layers } pub fn launch_test(&self) -> bool { - self.inner().launch_test.get() + self.inner().opt.launch_test } } diff --git a/src/keyboard.rs b/src/keyboard.rs index 4166f22b..21ad6564 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -16,7 +16,7 @@ use std::{ }; use crate::{show_error_dialog, Backlight, KeyboardLayer, MainWindow, Page, Picker, Testing}; -use backend::{Board, DerefCell, KeyMap, Layout, Mode}; +use backend::{Board, DerefCell, KeyMap, Layout}; use widgets::SelectedKeys; #[derive(Default)] @@ -273,16 +273,6 @@ impl Keyboard { &self.inner().board } - pub fn display_name(&self) -> String { - let name = &self.layout().meta.display_name; - let model = self.board().model().splitn(2, '/').nth(1).unwrap(); - if self.board().is_fake() { - format!("{} ({})", name, fl!("board-fake", model = model)) - } else { - format!("{} ({})", name, model) - } - } - fn layout(&self) -> &Layout { &self.inner().board.layout() } @@ -336,67 +326,13 @@ impl Keyboard { } let _loader = self.toplevel().and_then(|x| { - Some( - x.downcast_ref::()? - .display_loader(&fl!("loading-keyboard", keyboard = self.display_name())), - ) + Some(x.downcast_ref::()?.display_loader(&fl!( + "loading-keyboard", + keyboard = self.board().display_name() + ))) }); - let key_indices = self - .board() - .keys() - .iter() - .enumerate() - .map(|(i, k)| (&k.logical_name, i)) - .collect::>(); - - let futures = FuturesUnordered::>>>::new(); - - for (k, v) in &keymap.map { - for (layer, scancode_name) in v.iter().enumerate() { - let n = key_indices[&k]; - futures.push(Box::pin(async move { - if let Err(err) = self.board().keys()[n] - .set_scancode(layer, scancode_name) - .await - { - error!("{}: {:?}", fl!("error-set-keymap"), err); - } - })); - } - } - - for (k, hs) in &keymap.key_leds { - let res = self.board().keys()[key_indices[&k]].set_color(*hs); - futures.push(Box::pin(async move { - if let Err(err) = res.await { - error!("{}: {}", fl!("error-key-led"), err); - } - })); - } - - for (i, keymap_layer) in keymap.layers.iter().enumerate() { - let layer = &self.board().layers()[i]; - if let Some((mode, speed)) = keymap_layer.mode { - futures.push(Box::pin(async move { - if let Err(err) = layer.set_mode(Mode::from_index(mode).unwrap(), speed).await { - error!("{}: {}", fl!("error-set-layer-mode"), err) - } - })); - } - futures.push(Box::pin(async move { - if let Err(err) = layer.set_brightness(keymap_layer.brightness).await { - error!("{}: {}", fl!("error-set-layer-brightness"), err) - } - })); - futures.push(Box::pin(async move { - if let Err(err) = layer.set_color(keymap_layer.color).await { - error!("{}: {}", fl!("error-set-layer-color"), err) - } - })); - } - - futures.collect::<()>().await; + self.board().import_keymap(keymap).await; } fn import(&self) { diff --git a/src/main.rs b/src/main.rs index 205cec00..14f4569d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ use std::process; mod about_dialog; mod backlight; +mod cli; mod configurator_app; mod error_dialog; mod keyboard; diff --git a/src/main_window.rs b/src/main_window.rs index 59d4e731..24beb160 100644 --- a/src/main_window.rs +++ b/src/main_window.rs @@ -305,7 +305,7 @@ impl MainWindow { ..insert(pango::AttrInt::new_weight(pango::Weight::Bold)); }; let label = cascade! { - gtk::Label::new(Some(&keyboard.display_name())); + gtk::Label::new(Some(&keyboard.board().display_name())); ..set_attributes(Some(&attr_list)); }; let window = self;