diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ef09e16..b83d0a3 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -18,7 +18,7 @@ jobs: - uses: rui314/setup-mold@v1 - uses: awalsh128/cache-apt-pkgs-action@latest with: - packages: libdbus-1-dev libusb-dev libhidapi-dev libhidapi-hidraw0 pkg-config libudev-dev + packages: libdbus-1-dev libusb-dev libhidapi-dev libhidapi-hidraw0 pkg-config libudev-dev libgtk-3-dev version: 1.0 - name: "Install Rust" diff --git a/razer_control_gui/Cargo.lock b/razer_control_gui/Cargo.lock index b616e1b..4cd204e 100644 --- a/razer_control_gui/Cargo.lock +++ b/razer_control_gui/Cargo.lock @@ -50,6 +50,29 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "atk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" +dependencies = [ + "atk-sys", + "glib 0.18.5", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" +dependencies = [ + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -77,6 +100,31 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags", + "cairo-sys-rs", + "glib 0.18.5", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys 0.18.1", + "libc", + "system-deps", +] + [[package]] name = "cc" version = "1.0.83" @@ -86,6 +134,16 @@ dependencies = [ "libc", ] +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -120,10 +178,10 @@ version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", - "syn", + "syn 2.0.32", ] [[package]] @@ -155,6 +213,143 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset 0.9.1", + "rustc_version", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gdk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib 0.18.5", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib 0.18.5", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys 0.18.1", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys 0.18.1", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + [[package]] name = "getrandom" version = "0.2.10" @@ -166,12 +361,235 @@ dependencies = [ "wasi", ] +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys 0.18.1", + "glib 0.18.5", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "gio-sys" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4bdbef451b0f0361e7f762987cc6bebd5facab1d535e85a3cf1115dfb08db40" +dependencies = [ + "glib-sys 0.19.5", + "gobject-sys 0.19.5", + "libc", + "system-deps", + "windows-sys", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys 0.18.1", + "glib-macros 0.18.5", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib" +version = "0.19.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e52355166df21c7ed16b6a01f615669c7911ed74e27ef60eba339c0d2da12490" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys 0.19.5", + "glib-macros 0.19.7", + "glib-sys 0.19.5", + "gobject-sys 0.19.5", + "libc", + "memchr", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "glib-macros" +version = "0.19.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70025dbfa1275cf7d0531c3317ba6270dae15d87e63342229d638246ff45202e" +dependencies = [ + "heck 0.5.0", + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glib-sys" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "767d23ead9bbdfcbb1c2242c155c8128a7d13dde7bf69c176f809546135e2282" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys 0.18.1", + "libc", + "system-deps", +] + +[[package]] +name = "gobject-sys" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3787b0bfacca12bb25f8f822b0dbee9f7e4a86e6469a29976d332d2c14c945b" +dependencies = [ + "glib-sys 0.19.5", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib 0.18.5", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys 0.18.1", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hidapi" version = "2.4.1" @@ -186,6 +604,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "itoa" version = "1.0.9" @@ -225,9 +653,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76fc44e2588d5b436dbc3c6cf62aef290f90dab6235744a93dfe1cc18f451e2c" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memoffset" @@ -238,6 +666,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -253,7 +690,7 @@ dependencies = [ "bitflags", "cfg-if", "libc", - "memoffset", + "memoffset 0.7.1", "pin-utils", ] @@ -267,6 +704,43 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib 0.18.5", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + [[package]] name = "pin-utils" version = "0.1.0" @@ -285,6 +759,58 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.66" @@ -333,12 +859,27 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "ryu" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "serde" version = "1.0.188" @@ -365,7 +906,7 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.32", ] [[package]] @@ -379,6 +920,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +dependencies = [ + "serde", +] + [[package]] name = "service" version = "0.2.0" @@ -386,6 +936,8 @@ dependencies = [ "bincode", "clap", "dbus", + "glib 0.19.7", + "gtk", "hidapi", "lazy_static", "rand", @@ -415,6 +967,21 @@ dependencies = [ "libc", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + [[package]] name = "strsim" version = "0.10.0" @@ -423,15 +990,38 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "2.0.29" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml", + "version-compare", +] + [[package]] name = "systemstat" version = "0.2.3" @@ -446,6 +1036,32 @@ dependencies = [ "winapi", ] +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + +[[package]] +name = "thiserror" +version = "1.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3de26b0965292219b4287ff031fcba86837900fe9cd2b34ea8ad893c0953d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + [[package]] name = "time" version = "0.3.28" @@ -463,6 +1079,62 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml_datetime" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + [[package]] name = "udev" version = "0.7.0" @@ -486,6 +1158,18 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -579,3 +1263,12 @@ name = "windows_x86_64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] diff --git a/razer_control_gui/Cargo.toml b/razer_control_gui/Cargo.toml index 40dc454..e7577c8 100644 --- a/razer_control_gui/Cargo.toml +++ b/razer_control_gui/Cargo.toml @@ -15,6 +15,10 @@ path = "src/cli.rs" name = "daemon" path = "src/daemon.rs" +[[bin]] +name = "razer-settings" +path = "src/razer-settings/razer-settings.rs" + [dependencies] dbus = "0.9.7" serde = { version = "1.0", features = ["derive"] } @@ -27,3 +31,5 @@ systemstat = "0.2.3" hidapi = { version = "2.4.1", default-features = false, features = ["linux-native"] } serde-big-array = "0.5.1" clap = { version = "4.4.13", features = ["derive"] } +gtk = "0.18.1" +glib = "0.19.7" diff --git a/razer_control_gui/README.md b/razer_control_gui/README.md index 99b4bb4..27f5cec 100644 --- a/razer_control_gui/README.md +++ b/razer_control_gui/README.md @@ -2,11 +2,13 @@ ## Current features * Full background daemon - Auto load state on machine startup based on last configuration -* CLI application for adjusting basic settings +* CLI and GUI application for adjusting basic settings + +![](Screenshoot.png) ## Installing 1. Install cargo or rustc -2. add `libdbus-1-dev libusb-dev libhidapi-dev libhidapi-hidraw0 pkg-config libudev-dev` packages (or equivelent) +2. add `libdbus-1-dev libusb-dev libhidapi-dev libhidapi-hidraw0 pkg-config libudev-dev libgtk-3-dev` packages (or equivelent) 3. run `./install.sh install` as a normal user 4. reboot 5. Enjoy! diff --git a/razer_control_gui/Screenshoot.png b/razer_control_gui/Screenshoot.png new file mode 100644 index 0000000..5dd000c Binary files /dev/null and b/razer_control_gui/Screenshoot.png differ diff --git a/razer_control_gui/data/gui/razer-settings.desktop b/razer_control_gui/data/gui/razer-settings.desktop new file mode 100644 index 0000000..b7c10c4 --- /dev/null +++ b/razer_control_gui/data/gui/razer-settings.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Application +Name=Razer Settings +Comment=Manage settings from your Razer laptop +Exec=/usr/bin/razer-settings +Terminal=false diff --git a/razer_control_gui/install.sh b/razer_control_gui/install.sh index 702d33c..ade9d8a 100755 --- a/razer_control_gui/install.sh +++ b/razer_control_gui/install.sh @@ -12,7 +12,7 @@ detect_init_system() { install() { echo "Building the project..." - cargo build --release + cargo build --release # TODO: The GUI should be optional. At least for now. Before releasing this, it sould be turned into a feature with an explicit cli switch to install it if [ $? -ne 0 ]; then echo "An error occurred while building the project" @@ -36,6 +36,12 @@ install() { sudo bash < /dev/null 2>&1; then + # We only install the desktop file if there are already desktop + # files on the system + cp data/gui/razer-settings.desktop /usr/share/applications/ + fi cp target/release/daemon /usr/share/razercontrol/ cp data/devices/laptops.json /usr/share/razercontrol/ cp data/udev/99-hidraw-permissions.rules /etc/udev/rules.d/ @@ -75,6 +81,8 @@ uninstall() { echo "Uninstalling the files..." sudo bash < Option { } } +#[allow(dead_code)] +/// We use this from the app, but it should replace bind +pub fn try_bind() -> std::io::Result { + UnixStream::connect(SOCKET_PATH) +} + #[allow(dead_code)] pub fn create() -> Option { if let Ok(_) = std::fs::metadata(SOCKET_PATH) { diff --git a/razer_control_gui/src/kbd/mod.rs b/razer_control_gui/src/kbd/mod.rs index 667a50d..54a7309 100644 --- a/razer_control_gui/src/kbd/mod.rs +++ b/razer_control_gui/src/kbd/mod.rs @@ -23,7 +23,7 @@ pub struct EffectSave { } /// Base effect trait. -/// An effect is a lighting function that is updated 30 times per seonc +/// An effect is a lighting function that is updated 30 times per second /// in order to create an animation of some description on the laptop's /// keyboard pub trait Effect: Send + Sync { diff --git a/razer_control_gui/src/razer-settings/error_handling.rs b/razer_control_gui/src/razer-settings/error_handling.rs new file mode 100644 index 0000000..c383639 --- /dev/null +++ b/razer_control_gui/src/razer-settings/error_handling.rs @@ -0,0 +1,59 @@ +use gtk::prelude::*; +use gtk::{ApplicationWindow, DialogFlags, MessageDialog}; + +pub trait Crash { + type Value; + + /// Gets the good value or aborts the application with a msg. + fn or_crash(self, msg: impl AsRef) -> Self::Value; +} + +impl Crash for Option { + type Value = T; + + fn or_crash(self, msg: impl AsRef) -> Self::Value { + match self { + Self::Some(v) => v, + Self::None => crash_with_msg(msg) + } + } +} + +impl Crash for Result { + type Value = T; + + fn or_crash(self, msg: impl AsRef) -> Self::Value { + match self { + Self::Ok(v) => v, + Self::Err(_) => crash_with_msg(msg) + } + } +} + +pub fn crash_with_msg(msg: impl AsRef) -> ! { + let msg = msg.as_ref(); + show_msg(msg); + std::process::exit(1); +} + +fn show_msg(msg: impl AsRef) { + let msg = format!("{}.\n\nThis is an alpha!", msg.as_ref()); + + let msg_box = MessageDialog::new::( + None, DialogFlags::MODAL, + gtk::MessageType::Error, gtk::ButtonsType::Ok, + &msg + ); + msg_box.set_title("The application has crashed"); + + let _response = msg_box.run(); +} + +/// Installs a custom panic hook to display an error to the user +pub fn setup_panic_hook() { + let default_panic_hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |info| { + show_msg(info.to_string()); + default_panic_hook(info); + })); +} diff --git a/razer_control_gui/src/razer-settings/razer-settings.rs b/razer_control_gui/src/razer-settings/razer-settings.rs new file mode 100644 index 0000000..228ab3c --- /dev/null +++ b/razer_control_gui/src/razer-settings/razer-settings.rs @@ -0,0 +1,650 @@ +use std::io::ErrorKind; + +use gtk::prelude::*; +use gtk::{Application, ApplicationWindow}; +use gtk::{ + Box, Label, Scale, Stack, StackSwitcher, Switch, ToolItem, Toolbar, + ComboBoxText, Button, ColorButton +}; +use gtk::{glib, glib::clone}; + +// sudo apt install libgdk-pixbuf2.0-dev libcairo-dev libatk1.0-dev +// sudo apt install libpango1.0-dev + +#[path = "../comms.rs"] +mod comms; +mod error_handling; +mod widgets; +mod util; + +use error_handling::*; +use widgets::*; +use util::*; + +fn send_data(opt: comms::DaemonCommand) -> Option { + match comms::try_bind() { + Ok(socket) => comms::send_to_daemon(opt, socket), + Err(error) if error.kind() == ErrorKind::NotFound => { + crash_with_msg("Can't connect to the daemon"); + } + Err(error) => { + println!("Error opening socket: {error}"); + None + } + } +} + +fn get_bho() -> Option<(bool, u8)> { + let response = send_data(comms::DaemonCommand::GetBatteryHealthOptimizer())?; + + use comms::DaemonResponse::*; + match response { + GetBatteryHealthOptimizer { is_on, threshold } => { + Some((is_on, threshold)) + } + response => { + // This should not happen + println!("Instead of GetBatteryHealthOptimizer got {response:?}"); + None + } + } +} + +fn set_bho(is_on: bool, threshold: u8) -> Option { + let response = send_data(comms::DaemonCommand::SetBatteryHealthOptimizer { + is_on, threshold + })?; + + use comms::DaemonResponse::*; + match response { + SetBatteryHealthOptimizer { result } => { + Some(result) + } + response => { + // This should not happen + println!("Instead of SetBatteryHealthOptimizer got {response:?}"); + None + } + } +} + +fn get_brightness(ac: bool) -> Option { + let ac = if ac { 1 } else { 0 }; + let response = send_data(comms::DaemonCommand::GetBrightness{ ac })?; + + use comms::DaemonResponse::*; + match response { + GetBrightness { result } => { + Some(result) + } + response => { + // This should not happen + println!("Instead of GetBrightness got {response:?}"); + None + } + } +} + +fn set_brightness(ac: bool, val: u8) -> Option { + let ac = if ac { 1 } else { 0 }; + let response = send_data(comms::DaemonCommand::SetBrightness { ac, val })?; + + use comms::DaemonResponse::*; + match response { + SetBrightness { result } => { + Some(result) + } + response => { + // This should not happen + println!("Instead of SetBrightness got {response:?}"); + None + } + } +} + +fn get_logo(ac: bool) -> Option { + let ac = if ac { 1 } else { 0 }; + let response = send_data(comms::DaemonCommand::GetLogoLedState{ ac })?; + + use comms::DaemonResponse::*; + match response { + GetLogoLedState { logo_state } => { + Some(logo_state) + } + response => { + // This should not happen + println!("Instead of GetLogoLedState got {response:?}"); + None + } + } +} + +fn set_logo(ac: bool, logo_state: u8) -> Option { + let ac = if ac { 1 } else { 0 }; + let response = send_data(comms::DaemonCommand::SetLogoLedState{ ac , logo_state })?; + + use comms::DaemonResponse::*; + match response { + SetLogoLedState { result } => { + Some(result) + } + response => { + // This should not happen + println!("Instead of SetLogoLedState got {response:?}"); + None + } + } +} + +fn set_effect(name: &str, values: Vec) -> Option { + let response = send_data(comms::DaemonCommand::SetEffect { + name: name.into(), params: values + })?; + + use comms::DaemonResponse::*; + match response { + SetEffect { result } => { + Some(result) + } + response => { + // This should not happen + println!("Instead of SetEffect got {response:?}"); + None + } + } +} + +fn get_power(ac: bool) -> Option<(u8, u8, u8)> { + let ac = if ac { 1 } else { 0 }; + let mut result = (0, 0, 0); + + let response = send_data(comms::DaemonCommand::GetPwrLevel{ ac })?; + use comms::DaemonResponse::*; + match response { + GetPwrLevel { pwr } => { + result.0 = pwr; + } + response => { + // This should not happen + println!("Instead of GetPwrLevel got {response:?}"); + return None + } + } + + let response = send_data(comms::DaemonCommand::GetCPUBoost { ac })?; + use comms::DaemonResponse::*; + match response { + GetCPUBoost { cpu } => { + result.1 = cpu; + } + response => { + // This should not happen + println!("Instead of GetCPUBoost got {response:?}"); + return None + } + } + + let response = send_data(comms::DaemonCommand::GetGPUBoost { ac })?; + use comms::DaemonResponse::*; + match response { + GetGPUBoost { gpu } => { + result.2 = gpu; + } + response => { + // This should not happen + println!("Instead of GetGPUBoost got {response:?}"); + return None + } + } + + Some(result) +} + +fn set_power(ac: bool, power: (u8, u8, u8)) -> Option { + let ac = if ac { 1 } else { 0 }; + let response = send_data(comms::DaemonCommand::SetPowerMode { + ac, pwr: power.0, cpu: power.1, gpu: power.2 } + )?; + + use comms::DaemonResponse::*; + match response { + SetPowerMode { result } => { + Some(result) + } + response => { + // This should not happen + println!("Instead of SetPowerMode got {response:?}"); + None + } + } +} + +fn get_fan_speed(ac: bool) -> Option { + let ac = if ac { 1 } else { 0 }; + let response = send_data(comms::DaemonCommand::GetFanSpeed{ ac })?; + + use comms::DaemonResponse::*; + match response { + GetFanSpeed { rpm } => { + Some(rpm) + } + response => { + // This should not happen + println!("Instead of GetFanSpeed got {response:?}"); + None + } + } +} + +fn set_fan_speed(ac: bool, value: i32) -> Option { + let ac = if ac { 1 } else { 0 }; + let response = send_data(comms::DaemonCommand::SetFanSpeed{ ac, rpm: value })?; + + use comms::DaemonResponse::*; + match response { + SetFanSpeed { result } => { + Some(result) + } + response => { + // This should not happen + println!("Instead of SetFanSpeed got {response:?}"); + None + } + } +} + +fn main() { + setup_panic_hook(); + gtk::init().or_crash("Failed to initialize GTK."); + + let app = Application::builder() + .application_id("com.example.hello") + .build(); + + app.connect_activate(move |app| { + let window = ApplicationWindow::builder() + .application(app) + .default_width(640) + .default_height(480) + .title("Razer Settings") + .build(); + + let ac_settings_page = make_page(true); + let battery_settings_page = make_page(false); + let general_page = make_general_page(); + + let stack = Stack::new(); + stack.set_transition_type(gtk::StackTransitionType::SlideLeftRight); + + stack.add_titled(&ac_settings_page.master_container, "AC", "AC"); + stack.add_titled(&battery_settings_page.master_container, "Battery", "Battery"); + stack.add_titled(&general_page.master_container, "General", "General"); + + stack.connect_screen_changed(|_, _| { + println!("Page changed"); + }); + + let stack_switcher = StackSwitcher::builder() + .orientation(gtk::Orientation::Horizontal) + .build(); + + stack_switcher.set_stack(Some(&stack)); + stack_switcher.set_halign(gtk::Align::Center); + stack_switcher.connect_screen_changed(|_, _| { + println!("Page changed"); + }); + + let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); + let toolbar = Toolbar::new(); + toolbar.style_context().add_class("primary-toolbar"); + vbox.pack_start(&toolbar, false, false, 0); + vbox.pack_start(&stack, true, true, 0); + // header_bar.set_title(Some("Razer Settings")); + // header_bar.set_child(Some(&stack_switcher)); + // window.set_titlebar(Some(&header_bar)); + let tool_item = ToolItem::new(); + gtk::prelude::ToolItemExt::set_expand(&tool_item, true); + tool_item.style_context().add_class("raised"); + let stask_switcher_holder = Box::new(gtk::Orientation::Horizontal, 0); + stask_switcher_holder.set_border_width(1); + stask_switcher_holder.pack_start(&stack_switcher, true, true, 0); + tool_item.add(&stask_switcher_holder); + toolbar.insert(&tool_item, 0); + + window.set_child(Some(&vbox)); + + window.show_all(); + + // If we know we are not running on AC, we show the battery tab by + // default + match check_if_running_on_ac_power() { + Some(false) => stack.set_visible_child_name("Battery"), + _ => {} + } + }); + + app.run(); +} + +fn make_page(ac: bool) -> SettingsPage { + let logo = get_logo(ac); + let fan_speed = get_fan_speed(ac).or_crash("Error reading fan speed"); + let brightness = get_brightness(ac).or_crash("Error reading brightness"); + let power = get_power(ac); + + let settings_page = SettingsPage::new(); + + // Logo section + if let Some(logo) = logo { + let settings_section = settings_page.add_section(Some("Logo")); + let label = Label::new(Some("Turn on logo")); + let logo_options = ComboBoxText::new(); + logo_options.append_text("Off"); + logo_options.append_text("On"); + logo_options.append_text("Breathing"); + logo_options.set_active(Some(logo as u32)); + logo_options.connect_changed(move |options| { + let logo = options.active().or_crash("Illegal state") as u8; + set_logo(ac, logo); + let logo = get_logo(ac).or_crash("Error reading logo").clamp(0, 2); + options.set_active(Some(logo as u32)); + }); + let row = SettingsRow::new(&label, &logo_options); + settings_section.add_row(&row.master_container); + } + + // Power section + if let Some(power) = power { + let settings_section = settings_page.add_section(Some("Power")); + let label = Label::new(Some("Power Profile")); + let power_profile = ComboBoxText::new(); + power_profile.append_text("Balanced"); + power_profile.append_text("Gaming"); + power_profile.append_text("Creator"); + power_profile.append_text("Silent"); + power_profile.append_text("Custom"); + power_profile.set_active(Some(power.0 as u32)); + power_profile.set_width_request(100); + let row = SettingsRow::new(&label, &power_profile); + settings_section.add_row(&row.master_container); + let label = Label::new(Some("CPU Boost")); + let cpu_boost = ComboBoxText::new(); + cpu_boost.append_text("Low"); + cpu_boost.append_text("Medium"); + cpu_boost.append_text("High"); + cpu_boost.append_text("Boost"); + cpu_boost.set_active(Some(power.1 as u32)); + cpu_boost.set_width_request(100); + let row = SettingsRow::new(&label, &cpu_boost); + let cpu_boost_row = &row.master_container; + settings_section.add_row(cpu_boost_row); + let label = Label::new(Some("GPU Boost")); + let gpu_boost = ComboBoxText::new(); + gpu_boost.append_text("Low"); + gpu_boost.append_text("Medium"); + gpu_boost.append_text("High"); + gpu_boost.set_active(Some(power.2 as u32)); + gpu_boost.set_width_request(100); + let row = SettingsRow::new(&label, &gpu_boost); + let gpu_boost_row = &row.master_container; + settings_section.add_row(gpu_boost_row); + settings_section.add_row(&row.master_container); + + cpu_boost_row.show_all(); + cpu_boost_row.set_no_show_all(true); + gpu_boost_row.show_all(); + gpu_boost_row.set_no_show_all(true); + if power.0 == 4 { + cpu_boost_row.set_visible(true); + gpu_boost_row.set_visible(true); + } else { + cpu_boost_row.set_visible(false); + gpu_boost_row.set_visible(false); + } + + power_profile.connect_changed(clone!( + @weak cpu_boost, @weak gpu_boost, + @weak cpu_boost_row, @weak gpu_boost_row + => + move |power_profile| { + let profile = power_profile.active().or_crash("Illegal state") as u8; + let cpu = cpu_boost.active().or_crash("Illegal state") as u8; + let gpu = gpu_boost.active().or_crash("Illegal state") as u8; + set_power(ac, (profile, cpu, gpu)).or_crash("Error setting power"); + + let power = get_power(ac).or_crash("Error reading power"); + power_profile.set_active(Some(power.0 as u32)); + cpu_boost.set_active(Some(power.1 as u32)); + gpu_boost.set_active(Some(power.2 as u32)); + + if power.0 == 4 { + cpu_boost_row.set_visible(true); + gpu_boost_row.set_visible(true); + } else { + cpu_boost_row.set_visible(false); + gpu_boost_row.set_visible(false); + } + } + )); + cpu_boost.connect_changed(clone!( + @weak power_profile, @weak gpu_boost + => + move |cpu_boost| { + let profile = power_profile.active().or_crash("Illegal state") as u8; + let cpu = cpu_boost.active().or_crash("Illegal state") as u8; + let gpu = gpu_boost.active().or_crash("Illegal state") as u8; + set_power(ac, (profile, cpu, gpu)).or_crash("Error setting power"); + + let power = get_power(ac).or_crash("Error reading power"); + power_profile.set_active(Some(power.0 as u32)); + cpu_boost.set_active(Some(power.1 as u32)); + gpu_boost.set_active(Some(power.2 as u32)); + } + )); + gpu_boost.connect_changed(clone!( + @weak power_profile, @weak cpu_boost + => + move |gpu_boost| { + let profile = power_profile.active().or_crash("Illegal state") as u8; + let cpu = cpu_boost.active().or_crash("Illegal state") as u8; + let gpu = gpu_boost.active().or_crash("Illegal state") as u8; + set_power(ac, (profile, cpu, gpu)).or_crash("Error setting power"); + + let power = get_power(ac).or_crash("Error reading power"); + power_profile.set_active(Some(power.0 as u32)); + cpu_boost.set_active(Some(power.1 as u32)); + gpu_boost.set_active(Some(power.2 as u32)); + } + )); + } + + // Fan Speed Section + let settings_section = settings_page.add_section(Some("Fan Speed")); + let label = Label::new(Some("Auto")); + let switch = Switch::new(); + let auto = fan_speed == 0; + switch.set_state(auto); + let row = SettingsRow::new(&label, &switch); + settings_section.add_row(&row.master_container); + let label = Label::new(Some("Fan Speed")); + let scale = Scale::with_range(gtk::Orientation::Horizontal, 3500f64, 5000f64, 1f64); + scale.set_value(fan_speed as f64); + scale.set_sensitive(fan_speed != 0); + scale.set_width_request(100); + scale.connect_change_value(clone!(@weak switch => @default-return gtk::glib::Propagation::Stop, move |scale, stype, value| { + let value = value.clamp(3500f64, 5000f64); + set_fan_speed(ac, value as i32).or_crash("Error setting fan speed"); + let fan_speed = get_fan_speed(ac).or_crash("Error reading fan speed"); + let auto = fan_speed == 0; + scale.set_value(fan_speed as f64); + scale.set_sensitive(!auto); + switch.set_state(auto); + return gtk::glib::Propagation::Stop; + })); + switch.connect_changed_active(clone!(@weak scale => move |switch| { + set_fan_speed(ac, if switch.is_active() { 0 } else { 3500 }).or_crash("Error setting fan speed"); + let fan_speed = get_fan_speed(ac).or_crash("Error reading fan speed"); + let auto = fan_speed == 0; + scale.set_value(fan_speed as f64); + scale.set_sensitive(!auto); + switch.set_state(auto); + })); + let row = SettingsRow::new(&label, &scale); + settings_section.add_row(&row.master_container); + + // Keyboard Section + let settings_section = settings_page.add_section(Some("Keyboard")); + let label = Label::new(Some("Brightness")); + let scale = Scale::with_range(gtk::Orientation::Horizontal, 0f64, 100f64, 1f64); + scale.set_value(brightness as f64); + scale.set_width_request(100); + scale.connect_change_value(move |scale, stype, value| { + let value = value.clamp(0f64, 100f64); + set_brightness(ac, value as u8).or_crash("Error setting brigthness"); + let brightness = get_brightness(ac).or_crash("Error reading brightness"); + scale.set_value(brightness as f64); + return gtk::glib::Propagation::Stop; + }); + let row = SettingsRow::new(&label, &scale); + settings_section.add_row(&row.master_container); + + settings_page +} + +fn make_general_page() -> SettingsPage { + let bho = get_bho(); + + let page = SettingsPage::new(); + + // Keyboard Section + let settings_section = page.add_section(Some("Keyboard")); + let label = Label::new(Some("Effect")); + let effect_options = ComboBoxText::new(); + effect_options.append_text("Static"); + effect_options.append_text("Static Gradient"); + effect_options.append_text("Wave Gradient"); + effect_options.append_text("Breathing"); + effect_options.set_active(Some(0)); + let row = SettingsRow::new(&label, &effect_options); + settings_section.add_row(&row.master_container); + let label = Label::new(Some("Color 1")); + let color_picker = ColorButton::new(); + let row = SettingsRow::new(&label, &color_picker); + settings_section.add_row(&row.master_container); + let label = Label::new(Some("Color 2")); + let color_picker_2 = ColorButton::new(); + let row = SettingsRow::new(&label, &color_picker_2); + settings_section.add_row(&row.master_container); + let label = Label::new(Some("Write effect")); + let button = Button::with_label("Write"); + button.connect_clicked(clone!(@weak effect_options, @weak color_picker, @weak color_picker_2 => + move |_| { + let color = color_picker.color(); + let red = (color.red / 256) as u8; + let green = (color.green / 256) as u8; + let blue = (color.blue / 256) as u8; + + let color = color_picker_2.color(); + let red2 = (color.red / 256) as u8; + let green2 = (color.green / 256) as u8; + let blue2 = (color.blue / 256) as u8; + + let effect = effect_options.active().or_crash("Illegal state"); + match effect { + 0 => { + set_effect("static", vec![red, green, blue]) + .or_crash("Failed to set effect"); + }, + 1 => { + set_effect( + "static_gradient", + vec![red, green, blue, red2, green2, blue2] + ).or_crash("Failed to set effect"); + }, + 2 => { + set_effect("wave_gradient", + vec![red, green, blue, red2, green2, blue2] + ).or_crash("Failed to set effect"); + } + 3 => { + set_effect( + "breathing_single", + vec![red, green, blue, 10] + ).or_crash("Failed to set effect"); + } + _ => {} + } + } + )); + let row = SettingsRow::new(&label, &button); + settings_section.add_row(&row.master_container); + + + effect_options.connect_changed(clone!(@weak color_picker, @weak color_picker_2 => + move |options| { + let logo = options.active().or_crash("Illegal state"); // Unwrap: There is always one active + + match logo { + 0 => { + // TODO: Color 1 visible + }, + 1 => { + // TODO: Color 1 and 2 visible + }, + 2 => { + // TODO: Color 1 and 2 visible + } + 3 => { + // TODO: Color 1, 2, and duration visible + } + _ => {} + } + } + )); + + // Battery Health Optimizer section + if let Some(bho) = bho { + let settings_section = page.add_section(Some("Battery Health Optimizer")); + let label = Label::new(Some("Enable Battery Health Optimizer")); + let switch = Switch::new(); + switch.set_state(bho.0); + let row = SettingsRow::new(&label, &switch); + settings_section.add_row(&row.master_container); + let label = Label::new(Some("Theshold")); + let scale = Scale::with_range(gtk::Orientation::Horizontal, 65f64, 80f64, 1f64); + scale.set_value(bho.1 as f64); + scale.set_width_request(100); + scale.connect_change_value(clone!(@weak switch => @default-return gtk::glib::Propagation::Stop, move |scale, stype, value| { + let is_on = switch.is_active(); + let threshold = value.clamp(50f64, 80f64) as u8; + + set_bho(is_on, threshold).or_crash("Error setting bho"); + + let (is_on, threshold) = get_bho().or_crash("Error reading bho"); + + scale.set_value(threshold as f64); + scale.set_visible(is_on); + scale.set_sensitive(is_on); + + return gtk::glib::Propagation::Stop; + })); + scale.set_sensitive(bho.0); + switch.connect_changed_active(clone!(@weak scale => move |switch| { + let is_on = switch.is_active(); + let threshold = scale.value().clamp(50f64, 80f64) as u8; + + set_bho(is_on, threshold); // Ignoramos errores ya que leemos + // el resultado de vuelta + + let (is_on, threshold) = get_bho().or_crash("Error reading bho"); + + scale.set_value(threshold as f64); + scale.set_visible(is_on); + scale.set_sensitive(is_on); + })); + let row = SettingsRow::new(&label, &scale); + settings_section.add_row(&row.master_container); + } + + page +} diff --git a/razer_control_gui/src/razer-settings/util.rs b/razer_control_gui/src/razer-settings/util.rs new file mode 100644 index 0000000..c278d35 --- /dev/null +++ b/razer_control_gui/src/razer-settings/util.rs @@ -0,0 +1,13 @@ +/// This function attempts to determine whether we are running on AC power. +/// +/// At the moment it might not support every device or power supply. It is used +/// to determine which tab to show when loading the GUI. +pub fn check_if_running_on_ac_power() -> Option { + let result = std::fs::read("/sys/class/power_supply/AC0/online"); + + result.map(|contents| { + println!("contents: {contents:?}"); + if &contents == "1\n".as_bytes() { true } + else { false } + }).ok() +} diff --git a/razer_control_gui/src/razer-settings/widgets.rs b/razer_control_gui/src/razer-settings/widgets.rs new file mode 100644 index 0000000..5ee47d1 --- /dev/null +++ b/razer_control_gui/src/razer-settings/widgets.rs @@ -0,0 +1,150 @@ +use std::cell::Cell; + +use gtk::prelude::*; +use gtk::{ + Box, Frame, Label, ListBox, ListBoxRow, Separator, Widget, Grid +}; + +pub struct SettingsPage { + // TODO: Can I make this a widget? This is self originally + pub master_container: Box +} + +impl SettingsPage { + + pub fn new() -> SettingsPage { + let master_container = Box::new(gtk::Orientation::Vertical, 15); + master_container.set_margin_start(80); + master_container.set_margin_end(80); + master_container.set_margin_top(15); + master_container.set_margin_bottom(15); + + SettingsPage { + master_container + } + } + + pub fn add_section(&self, title: Option<&str>) -> SettingsSection { + let section = SettingsSection::new(title); + self.master_container.pack_start(§ion.master_container, false, false, 0); + section + } + +} + +pub struct SettingsRow { + // TODO: Can I make this a widget? This is self originally + pub master_container: ListBoxRow +} + +impl SettingsRow { + + pub fn new( + label: &impl IsA, + main_widget: &impl IsA, + // alternative_widget: Option<&impl IsA> + ) -> SettingsRow { + let master_container = ListBoxRow::new(); + + // TODO: Faltan cosas, hay un stack que IMO no tiene sentido por ahora + + let hbox = Box::new(gtk::Orientation::Horizontal, 0); + hbox.set_border_width(5); + hbox.set_margin_start(20); + hbox.set_margin_end(20); + // master_container.add(&hbox); + + let grid = Grid::new(); + grid.set_column_spacing(15); + // hbox.pack_start(&grid, true, true, 0); + + let description_box = Box::new(gtk::Orientation::Vertical, 0); + description_box.set_hexpand(true); + description_box.set_halign(gtk::Align::Start); + description_box.set_valign(gtk::Align::Center); + // self.label.props.xalign = 0.0 + description_box.add(label); + + grid.attach(&description_box, 0, 0, 1, 1); + grid.attach_next_to(main_widget /*stack*/, Some(&description_box), gtk::PositionType::Right, 1, 1); + hbox.add(&grid); // TODO: No es así como lo hacen + + master_container.add(&hbox); + + SettingsRow { + master_container + } + } + + pub fn add_section(&self, title: Option<&str>) -> SettingsSection { + let section = SettingsSection::new(title); + // self.master_container.pack_start(§ion.master_container, false, false, 0); TODO: It should be this + self.master_container.add(§ion.master_container); + section + } + +} + +pub struct SettingsSection { + // TODO: Can I make this a widget? This is self originally + pub master_container: Box, + container: Box, + frame: Frame, + need_separator: Cell +} + +impl SettingsSection { + + pub fn new(title: Option<&str>) -> SettingsSection { + let master_container = Box::new(gtk::Orientation::Vertical, 10); + + if let Some(title) = title { + let header_box = Box::new(gtk::Orientation::Vertical, 0); + header_box.set_spacing(5); + master_container.add(&header_box); + + let label = Label::new(None); + label.set_markup(&format!("{}", title)); + // Aligmnent 0, 0.5 + label.set_halign(gtk::Align::Start); + header_box.add(&label); + } + + let frame = Frame::new(None); + frame.set_shadow_type(gtk::ShadowType::In); + frame.style_context().add_class("view"); + // bho_frame.set_hexpand(true); + // Algo de size group + + let container = Box::new(gtk::Orientation::Vertical, 0); + frame.add(&container); + + SettingsSection { + master_container, + container, + frame, + need_separator: Cell::new(false) + } + } + + pub fn add_row(&self, widget: &impl IsA) { + let vbox = Box::new(gtk::Orientation::Vertical, 0); + + if self.need_separator.get() { + let separator = Separator::new(gtk::Orientation::Horizontal); + vbox.add(&separator); + } + + let list_box = ListBox::new(); + list_box.set_selection_mode(gtk::SelectionMode::None); + list_box.add(widget); + vbox.add(&list_box); + self.container.add(&vbox); + + if self.frame.parent().is_none() { + self.master_container.add(&self.frame); + } + + self.need_separator.set(true); + } +}