commit 78d361446d1b4236987245a1988e745915401e10 Author: d Date: Mon Aug 10 08:00:21 2026 +0700 Fork of pop-os/cosmic-session @ upstream diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..e6ca28b --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,8 @@ +- [ ] I have disclosed use of any AI generated code in my commit messages. + - If you are using an LLM, and do not fully understand the changes it is making to the code base, do not create a PR. + - In our experience, AI generated code often results in overly complex code that lacks enough context for a proper fix or feature inclusion. This results in considerably longer code reviews. Due to this, AI authored or partially authored PRs may be closed without comment. +- [ ] I understand these changes in full and will be able to respond to review comments. +- [ ] My change is accurately described in the commit message. +- [ ] My contribution is tested and working as described. +- [ ] I have read the [Developer Certificate of Origin](https://developercertificate.org/) and certify my contribution under its conditions. + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..40c608c --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# These are backup files generated by rustfmt +**/*.rs.bk + +# Added by cargo +/target + +# Added by Nix +/result + +# Debian packaging stuff +.cargo +vendor/ +vendor.tar +*.xz +*.deb +*.ddeb +*.dsc +*.changes +*.deb.tar.* +*.buildinfo +*.build +debian/* +!debian/*install +!debian/*postinst +!debian/*gsettings-override +!debian/changelog +!debian/control +!debian/links +!debian/rules +!debian/source diff --git a/.zed/settings.json b/.zed/settings.json new file mode 100644 index 0000000..2cc7b98 --- /dev/null +++ b/.zed/settings.json @@ -0,0 +1,15 @@ +{ + "format_on_save": "on", + "lsp": { + "rust-analyzer": { + "initialization_options": { + "check": { + "command": "clippy", + }, + "rustfmt": { + "extraArgs": ["+nightly"], + }, + }, + }, + }, +} diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c37f2ce --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1887 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "windows-sys", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-signal" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link", +] + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cc" +version = "1.2.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "color-eyre" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "cosmic-dbus-a11y" +version = "0.1.0" +source = "git+https://github.com/pop-os/dbus-settings-bindings#507e342c21d3ce6ae41b1d4f3fa2f0ad5ee23e75" +dependencies = [ + "zbus", +] + +[[package]] +name = "cosmic-session" +version = "1.0.0" +dependencies = [ + "color-eyre", + "cosmic-dbus-a11y", + "dirs", + "freedesktop-desktop-entry", + "futures-util", + "launch-pad", + "log-panics", + "logind-zbus", + "rustix", + "scopeguard", + "serde", + "serde_json", + "shell-words", + "tokio", + "tokio-util", + "tracing", + "tracing-journald", + "tracing-subscriber", + "zbus", + "zbus_systemd", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys", +] + +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "freedesktop-desktop-entry" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28273c5c6b97a5f07724f6652f064c0c7f637f9aa5e7c09c83bc3bc4ad4ea245" +dependencies = [ + "bstr", + "gettext-rs", + "log", + "memchr", + "thiserror", + "unicase", + "xdg", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "gettext-rs" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5857dc1b7f0fee86961de833f434e29494d72af102ce5355738c0664222bdf" +dependencies = [ + "gettext-sys", + "locale_config", +] + +[[package]] +name = "gettext-sys" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea859ab0dd7e70ff823032b3e077d03d39c965d68c6c10775add60e999d8ee9" +dependencies = [ + "cc", + "temp-dir", +] + +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indenter" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.0", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "launch-pad" +version = "1.0.1" +source = "git+https://github.com/pop-os/launch-pad#5b516ee6a012b5ad4a5acc0cf51b4472fc6733af" +dependencies = [ + "libc", + "log", + "rand", + "rustix", + "slotmap", + "sync_wrapper", + "thiserror", + "tokio", + "tokio-util", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libredox" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +dependencies = [ + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "locale_config" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d2c35b16f4483f6c26f0e4e9550717a2f6575bcd6f12a53ff0c490a94a6934" +dependencies = [ + "lazy_static", + "objc", + "objc-foundation", + "regex", + "winapi", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "log-panics" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f9dd8546191c1850ecf67d22f5ff00a935b890d0e84713159a55495cc2ac5f" +dependencies = [ + "backtrace", + "log", +] + +[[package]] +name = "logind-zbus" +version = "5.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469c962578b549a82f3d0cc72d0f77d1123780fa7121e2b03d78b0780f6ccac6" +dependencies = [ + "serde", + "zbus", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "owo-colors" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustc-demangle" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shell-words" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "slotmap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + +[[package]] +name = "temp-dir" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83176759e9416cf81ee66cb6508dbfe9c96f20b8b56265a39917551c23c70964" + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tokio" +version = "1.52.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "tracing", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.11+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-journald" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3a81ed245bfb62592b1e2bc153e77656d94ee6a0497683a65a12ccaf2438d0" +dependencies = [ + "libc", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "uds_windows" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" +dependencies = [ + "memoffset", + "tempfile", + "windows-sys", +] + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "uuid" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +dependencies = [ + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "xdg" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fb433233f2df9344722454bc7e96465c9d03bff9d77c248f9e7523fe79585b5" + +[[package]] +name = "zbus" +version = "5.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3bcbf15c8708d7fc1be0c993622e0a5cbd5e8b52bfa40afa4c3e0cd8d724ac1" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "libc", + "ordered-stream", + "rustix", + "serde", + "serde_repr", + "tokio", + "tracing", + "uds_windows", + "uuid", + "windows-sys", + "winnow", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51fa5406ad9175a8c825a931f8cf347116b531b3634fcb0b627c290f1f2516ff" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" +dependencies = [ + "serde", + "winnow", + "zvariant", +] + +[[package]] +name = "zbus_systemd" +version = "0.26000.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c8310483ff4a77492d04a90bef30f40938059b4a4b4f7e456dda335d816cb6" +dependencies = [ + "serde", + "zbus", +] + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zvariant" +version = "5.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db0ecb8987cf5e92653c57c098f7f0e39a03112edb796f4fe089fb7eaa14ff" +dependencies = [ + "endi", + "enumflags2", + "serde", + "winnow", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b949b639ab1b4bed763aa7481ba0e368af68d8b55532f8ed4bec86a59f2ca98" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d464f5733ffa07a3164d656f18533caace9d0638596721355d73256a410d691" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn", + "winnow", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..136af09 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,51 @@ +[package] +name = "cosmic-session" +description = "The session manager for the COSMIC desktop environment" +version = "1.0.0" +license = "GPL-3.0-only" +edition = "2024" +rust-version = "1.93" +authors = ["Lucy "] +publish = false + +[dependencies] +color-eyre = "0.6" +futures-util = "0.3" +cosmic-dbus-a11y = { git = "https://github.com/pop-os/dbus-settings-bindings" } +freedesktop-desktop-entry = { version = "0.8", optional = true } +shell-words = { version = "1.1.1", optional = true } +dirs = { version = "6.0.0", optional = true } +launch-pad = { git = "https://github.com/pop-os/launch-pad" } +log-panics = { version = "2", features = ["with-backtrace"] } +rustix = "1.1" +scopeguard = "1" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +tokio = { version = "1", features = [ + "fs", + "io-util", + "io-std", + "macros", + "net", + "parking_lot", + "process", + "rt", + "signal", + "sync", + "time", +] } +zbus_systemd = { version = "0.26000.0", optional = true, features = [ + "systemd1", +] } +tokio-util = "0.7" +tracing = "0.1" +tracing-journald = { version = "0.3", optional = true } +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +zbus = { version = "5.14.0", default-features = false, features = ["tokio"] } +logind-zbus = { version = "5.3.2", optional = true } + +[features] +systemd = ["dep:zbus_systemd", "dep:tracing-journald"] +logind = ["systemd", "logind-zbus"] +default = ["logind"] +autostart = ["dep:shell-words", "dep:dirs", "dep:freedesktop-desktop-entry"] diff --git a/Justfile b/Justfile new file mode 100644 index 0000000..6237df6 --- /dev/null +++ b/Justfile @@ -0,0 +1,75 @@ +rootdir := '' +prefix := '/usr' +cargo-target-dir := env('CARGO_TARGET_DIR', 'target') +orca := '/usr/bin/orca' +cosmic_dconf_profile := prefix + '/share/dconf/profile/cosmic' +usrdir := absolute_path(clean(rootdir / prefix)) +bindir := usrdir / 'bin' +systemddir := usrdir / 'lib' / 'systemd' / 'user' +sessiondir := usrdir / 'share' / 'wayland-sessions' +applicationdir := usrdir / 'share' / 'applications' + +default: build-release + +build-debug *args: + ORCA={{ orca }} cargo build {{ args }} + +# Compile with release profile +build-release *args: (build-debug '--release' args) + +# Compile with a vendored tarball +build-vendored *args: vendor-extract (build-release '--frozen --offline' args) + +# Remove Cargo build artifacts +clean: + cargo clean + +# Also remove .cargo and vendored dependencies +clean-dist: clean + rm -rf .cargo vendor vendor.tar target + +# Installs files into the system +install: + echo {{ cosmic_dconf_profile }} + # main binary + install -Dm0755 {{ cargo-target-dir }}/release/cosmic-session {{ bindir }}/cosmic-session + + # session start script + install -Dm0755 data/start-cosmic {{ bindir }}/start-cosmic + sed -i "s|DCONF_PROFILE=cosmic|DCONF_PROFILE={{ cosmic_dconf_profile }}|" {{ bindir }}/start-cosmic + + # systemd target + install -Dm0644 data/cosmic-session.target {{ systemddir }}/cosmic-session.target + + # session + install -Dm0644 data/cosmic.desktop {{ sessiondir }}/cosmic.desktop + + # mimeapps + install -Dm0644 data/cosmic-mimeapps.list {{ applicationdir }}/cosmic-mimeapps.list + + # dconf profile + install -Dm644 data/dconf/profile/cosmic {{ rootdir }}/{{ cosmic_dconf_profile }} + +# Vendor Cargo dependencies locally +vendor: + mkdir -p .cargo + cargo vendor | head -n -1 > .cargo/config.toml + echo 'directory = "vendor"' >> .cargo/config.toml + tar pcf vendor.tar vendor + rm -rf vendor + +# Extracts vendored dependencies +[private] +vendor-extract: + rm -rf vendor + tar pxf vendor.tar + +# Bump cargo version, create git commit, and create tag +tag version: + find -type f -name Cargo.toml -exec sed -i '0,/^version/s/^version.*/version = "{{ version }}"/' '{}' \; -exec git add '{}' \; + cargo check + cargo clean + git add Cargo.lock + git commit -m 'release: {{ version }}' + git commit --amend + git tag -a {{ version }} -m '' diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..175443c --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,595 @@ +GNU General Public License +========================== + +_Version 3, 29 June 2007_ +_Copyright © 2007 Free Software Foundation, Inc. <>_ + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +## Preamble + +The GNU General Public License is a free, copyleft license for software and other +kinds of works. + +The licenses for most software and other practical works are designed to take away +your freedom to share and change the works. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change all versions of a +program--to make sure it remains free software for all its users. We, the Free +Software Foundation, use the GNU General Public License for most of our software; it +applies also to any other work released this way by its authors. You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General +Public Licenses are designed to make sure that you have the freedom to distribute +copies of free software (and charge for them if you wish), that you receive source +code or can get it if you want it, that you can change the software or use pieces of +it in new free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or +asking you to surrender the rights. Therefore, you have certain responsibilities if +you distribute copies of the software, or if you modify it: responsibilities to +respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, +you must pass on to the recipients the same freedoms that you received. You must make +sure that they, too, receive or can get the source code. And you must show them these +terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: **(1)** assert +copyright on the software, and **(2)** offer you this License giving you legal permission +to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is +no warranty for this free software. For both users' and authors' sake, the GPL +requires that modified versions be marked as changed, so that their problems will not +be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of +the software inside them, although the manufacturer can do so. This is fundamentally +incompatible with the aim of protecting users' freedom to change the software. The +systematic pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we have designed +this version of the GPL to prohibit the practice for those products. If such problems +arise substantially in other domains, we stand ready to extend this provision to +those domains in future versions of the GPL, as needed to protect the freedom of +users. + +Finally, every program is threatened constantly by software patents. States should +not allow patents to restrict development and use of software on general-purpose +computers, but in those that do, we wish to avoid the special danger that patents +applied to a free program could make it effectively proprietary. To prevent this, the +GPL assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. + +## TERMS AND CONDITIONS + +### 0. Definitions + +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this +License. Each licensee is addressed as “you”. “Licensees” and +“recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in +a fashion requiring copyright permission, other than the making of an exact copy. The +resulting work is called a “modified version” of the earlier work or a +work “based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on +the Program. + +To “propagate” a work means to do anything with it that, without +permission, would make you directly or secondarily liable for infringement under +applicable copyright law, except executing it on a computer or modifying a private +copy. Propagation includes copying, distribution (with or without modification), +making available to the public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through a computer +network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the +extent that it includes a convenient and prominently visible feature that **(1)** +displays an appropriate copyright notice, and **(2)** tells the user that there is no +warranty for the work (except to the extent that warranties are provided), that +licensees may convey the work under this License, and how to view a copy of this +License. If the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +### 1. Source Code + +The “source code” for a work means the preferred form of the work for +making modifications to it. “Object code” means any non-source form of a +work. + +A “Standard Interface” means an interface that either is an official +standard defined by a recognized standards body, or, in the case of interfaces +specified for a particular programming language, one that is widely used among +developers working in that language. + +The “System Libraries” of an executable work include anything, other than +the work as a whole, that **(a)** is included in the normal form of packaging a Major +Component, but which is not part of that Major Component, and **(b)** serves only to +enable use of the work with that Major Component, or to implement a Standard +Interface for which an implementation is available to the public in source code form. +A “Major Component”, in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system (if any) on which +the executable work runs, or a compiler used to produce the work, or an object code +interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the +source code needed to generate, install, and (for an executable work) run the object +code and to modify the work, including scripts to control those activities. However, +it does not include the work's System Libraries, or general-purpose tools or +generally available free programs which are used unmodified in performing those +activities but which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for the work, and +the source code for shared libraries and dynamically linked subprograms that the work +is specifically designed to require, such as by intimate data communication or +control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate +automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +### 2. Basic Permissions + +All rights granted under this License are granted for the term of copyright on the +Program, and are irrevocable provided the stated conditions are met. This License +explicitly affirms your unlimited permission to run the unmodified Program. The +output from running a covered work is covered by this License only if the output, +given its content, constitutes a covered work. This License acknowledges your rights +of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without +conditions so long as your license otherwise remains in force. You may convey covered +works to others for the sole purpose of having them make modifications exclusively +for you, or provide you with facilities for running those works, provided that you +comply with the terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for you must do so +exclusively on your behalf, under your direction and control, on terms that prohibit +them from making any copies of your copyrighted material outside their relationship +with you. + +Conveying under any other circumstances is permitted solely under the conditions +stated below. Sublicensing is not allowed; section 10 makes it unnecessary. + +### 3. Protecting Users' Legal Rights From Anti-Circumvention Law + +No covered work shall be deemed part of an effective technological measure under any +applicable law fulfilling obligations under article 11 of the WIPO copyright treaty +adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention +of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of +technological measures to the extent such circumvention is effected by exercising +rights under this License with respect to the covered work, and you disclaim any +intention to limit operation or modification of the work as a means of enforcing, +against the work's users, your or third parties' legal rights to forbid circumvention +of technological measures. + +### 4. Conveying Verbatim Copies + +You may convey verbatim copies of the Program's source code as you receive it, in any +medium, provided that you conspicuously and appropriately publish on each copy an +appropriate copyright notice; keep intact all notices stating that this License and +any non-permissive terms added in accord with section 7 apply to the code; keep +intact all notices of the absence of any warranty; and give all recipients a copy of +this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer +support or warranty protection for a fee. + +### 5. Conveying Modified Source Versions + +You may convey a work based on the Program, or the modifications to produce it from +the Program, in the form of source code under the terms of section 4, provided that +you also meet all of these conditions: + +* **a)** The work must carry prominent notices stating that you modified it, and giving a +relevant date. +* **b)** The work must carry prominent notices stating that it is released under this +License and any conditions added under section 7. This requirement modifies the +requirement in section 4 to “keep intact all notices”. +* **c)** You must license the entire work, as a whole, under this License to anyone who +comes into possession of a copy. This License will therefore apply, along with any +applicable section 7 additional terms, to the whole of the work, and all its parts, +regardless of how they are packaged. This License gives no permission to license the +work in any other way, but it does not invalidate such permission if you have +separately received it. +* **d)** If the work has interactive user interfaces, each must display Appropriate Legal +Notices; however, if the Program has interactive interfaces that do not display +Appropriate Legal Notices, your work need not make them do so. + +A compilation of a covered work with other separate and independent works, which are +not by their nature extensions of the covered work, and which are not combined with +it such as to form a larger program, in or on a volume of a storage or distribution +medium, is called an “aggregate” if the compilation and its resulting +copyright are not used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work in an aggregate +does not cause this License to apply to the other parts of the aggregate. + +### 6. Conveying Non-Source Forms + +You may convey a covered work in object code form under the terms of sections 4 and +5, provided that you also convey the machine-readable Corresponding Source under the +terms of this License, in one of these ways: + +* **a)** Convey the object code in, or embodied in, a physical product (including a +physical distribution medium), accompanied by the Corresponding Source fixed on a +durable physical medium customarily used for software interchange. +* **b)** Convey the object code in, or embodied in, a physical product (including a +physical distribution medium), accompanied by a written offer, valid for at least +three years and valid for as long as you offer spare parts or customer support for +that product model, to give anyone who possesses the object code either **(1)** a copy of +the Corresponding Source for all the software in the product that is covered by this +License, on a durable physical medium customarily used for software interchange, for +a price no more than your reasonable cost of physically performing this conveying of +source, or **(2)** access to copy the Corresponding Source from a network server at no +charge. +* **c)** Convey individual copies of the object code with a copy of the written offer to +provide the Corresponding Source. This alternative is allowed only occasionally and +noncommercially, and only if you received the object code with such an offer, in +accord with subsection 6b. +* **d)** Convey the object code by offering access from a designated place (gratis or for +a charge), and offer equivalent access to the Corresponding Source in the same way +through the same place at no further charge. You need not require recipients to copy +the Corresponding Source along with the object code. If the place to copy the object +code is a network server, the Corresponding Source may be on a different server +(operated by you or a third party) that supports equivalent copying facilities, +provided you maintain clear directions next to the object code saying where to find +the Corresponding Source. Regardless of what server hosts the Corresponding Source, +you remain obligated to ensure that it is available for as long as needed to satisfy +these requirements. +* **e)** Convey the object code using peer-to-peer transmission, provided you inform +other peers where the object code and Corresponding Source of the work are being +offered to the general public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded from the +Corresponding Source as a System Library, need not be included in conveying the +object code work. + +A “User Product” is either **(1)** a “consumer product”, which +means any tangible personal property which is normally used for personal, family, or +household purposes, or **(2)** anything designed or sold for incorporation into a +dwelling. In determining whether a product is a consumer product, doubtful cases +shall be resolved in favor of coverage. For a particular product received by a +particular user, “normally used” refers to a typical or common use of +that class of product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected to use, the +product. A product is a consumer product regardless of whether the product has +substantial commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, +procedures, authorization keys, or other information required to install and execute +modified versions of a covered work in that User Product from a modified version of +its Corresponding Source. The information must suffice to ensure that the continued +functioning of the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for +use in, a User Product, and the conveying occurs as part of a transaction in which +the right of possession and use of the User Product is transferred to the recipient +in perpetuity or for a fixed term (regardless of how the transaction is +characterized), the Corresponding Source conveyed under this section must be +accompanied by the Installation Information. But this requirement does not apply if +neither you nor any third party retains the ability to install modified object code +on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to +continue to provide support service, warranty, or updates for a work that has been +modified or installed by the recipient, or for the User Product in which it has been +modified or installed. Access to a network may be denied when the modification itself +materially and adversely affects the operation of the network or violates the rules +and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with +this section must be in a format that is publicly documented (and with an +implementation available to the public in source code form), and must require no +special password or key for unpacking, reading or copying. + +### 7. Additional Terms + +“Additional permissions” are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. Additional +permissions that are applicable to the entire Program shall be treated as though they +were included in this License, to the extent that they are valid under applicable +law. If additional permissions apply only to part of the Program, that part may be +used separately under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any +additional permissions from that copy, or from any part of it. (Additional +permissions may be written to require their own removal in certain cases when you +modify the work.) You may place additional permissions on material, added by you to a +covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a +covered work, you may (if authorized by the copyright holders of that material) +supplement the terms of this License with terms: + +* **a)** Disclaiming warranty or limiting liability differently from the terms of +sections 15 and 16 of this License; or +* **b)** Requiring preservation of specified reasonable legal notices or author +attributions in that material or in the Appropriate Legal Notices displayed by works +containing it; or +* **c)** Prohibiting misrepresentation of the origin of that material, or requiring that +modified versions of such material be marked in reasonable ways as different from the +original version; or +* **d)** Limiting the use for publicity purposes of names of licensors or authors of the +material; or +* **e)** Declining to grant rights under trademark law for use of some trade names, +trademarks, or service marks; or +* **f)** Requiring indemnification of licensors and authors of that material by anyone +who conveys the material (or modified versions of it) with contractual assumptions of +liability to the recipient, for any liability that these contractual assumptions +directly impose on those licensors and authors. + +All other non-permissive additional terms are considered “further +restrictions” within the meaning of section 10. If the Program as you received +it, or any part of it, contains a notice stating that it is governed by this License +along with a term that is a further restriction, you may remove that term. If a +license document contains a further restriction but permits relicensing or conveying +under this License, you may add to a covered work material governed by the terms of +that license document, provided that the further restriction does not survive such +relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in +the relevant source files, a statement of the additional terms that apply to those +files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a +separately written license, or stated as exceptions; the above requirements apply +either way. + +### 8. Termination + +You may not propagate or modify a covered work except as expressly provided under +this License. Any attempt otherwise to propagate or modify it is void, and will +automatically terminate your rights under this License (including any patent licenses +granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a +particular copyright holder is reinstated **(a)** provisionally, unless and until the +copyright holder explicitly and finally terminates your license, and **(b)** permanently, +if the copyright holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently +if the copyright holder notifies you of the violation by some reasonable means, this +is the first time you have received notice of violation of this License (for any +work) from that copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of +parties who have received copies or rights from you under this License. If your +rights have been terminated and not permanently reinstated, you do not qualify to +receive new licenses for the same material under section 10. + +### 9. Acceptance Not Required for Having Copies + +You are not required to accept this License in order to receive or run a copy of the +Program. Ancillary propagation of a covered work occurring solely as a consequence of +using peer-to-peer transmission to receive a copy likewise does not require +acceptance. However, nothing other than this License grants you permission to +propagate or modify any covered work. These actions infringe copyright if you do not +accept this License. Therefore, by modifying or propagating a covered work, you +indicate your acceptance of this License to do so. + +### 10. Automatic Licensing of Downstream Recipients + +Each time you convey a covered work, the recipient automatically receives a license +from the original licensors, to run, modify and propagate that work, subject to this +License. You are not responsible for enforcing compliance by third parties with this +License. + +An “entity transaction” is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an organization, or +merging organizations. If propagation of a covered work results from an entity +transaction, each party to that transaction who receives a copy of the work also +receives whatever licenses to the work the party's predecessor in interest had or +could give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if the predecessor +has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or +affirmed under this License. For example, you may not impose a license fee, royalty, +or other charge for exercise of rights granted under this License, and you may not +initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging +that any patent claim is infringed by making, using, selling, offering for sale, or +importing the Program or any portion of it. + +### 11. Patents + +A “contributor” is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The work thus +licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or +controlled by the contributor, whether already acquired or hereafter acquired, that +would be infringed by some manner, permitted by this License, of making, using, or +selling its contributor version, but do not include claims that would be infringed +only as a consequence of further modification of the contributor version. For +purposes of this definition, “control” includes the right to grant patent +sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license +under the contributor's essential patent claims, to make, use, sell, offer for sale, +import and otherwise run, modify and propagate the contents of its contributor +version. + +In the following three paragraphs, a “patent license” is any express +agreement or commitment, however denominated, not to enforce a patent (such as an +express permission to practice a patent or covenant not to sue for patent +infringement). To “grant” such a patent license to a party means to make +such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the +Corresponding Source of the work is not available for anyone to copy, free of charge +and under the terms of this License, through a publicly available network server or +other readily accessible means, then you must either **(1)** cause the Corresponding +Source to be so available, or **(2)** arrange to deprive yourself of the benefit of the +patent license for this particular work, or **(3)** arrange, in a manner consistent with +the requirements of this License, to extend the patent license to downstream +recipients. “Knowingly relying” means you have actual knowledge that, but +for the patent license, your conveying the covered work in a country, or your +recipient's use of the covered work in a country, would infringe one or more +identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you +convey, or propagate by procuring conveyance of, a covered work, and grant a patent +license to some of the parties receiving the covered work authorizing them to use, +propagate, modify or convey a specific copy of the covered work, then the patent +license you grant is automatically extended to all recipients of the covered work and +works based on it. + +A patent license is “discriminatory” if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on the +non-exercise of one or more of the rights that are specifically granted under this +License. You may not convey a covered work if you are a party to an arrangement with +a third party that is in the business of distributing software, under which you make +payment to the third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties who would receive +the covered work from you, a discriminatory patent license **(a)** in connection with +copies of the covered work conveyed by you (or copies made from those copies), or **(b)** +primarily for and in connection with specific products or compilations that contain +the covered work, unless you entered into that arrangement, or that patent license +was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied +license or other defenses to infringement that may otherwise be available to you +under applicable patent law. + +### 12. No Surrender of Others' Freedom + +If conditions are imposed on you (whether by court order, agreement or otherwise) +that contradict the conditions of this License, they do not excuse you from the +conditions of this License. If you cannot convey a covered work so as to satisfy +simultaneously your obligations under this License and any other pertinent +obligations, then as a consequence you may not convey it at all. For example, if you +agree to terms that obligate you to collect a royalty for further conveying from +those to whom you convey the Program, the only way you could satisfy both those terms +and this License would be to refrain entirely from conveying the Program. + +### 13. Use with the GNU Affero General Public License + +Notwithstanding any other provision of this License, you have permission to link or +combine any covered work with a work licensed under version 3 of the GNU Affero +General Public License into a single combined work, and to convey the resulting work. +The terms of this License will continue to apply to the part which is the covered +work, but the special requirements of the GNU Affero General Public License, section +13, concerning interaction through a network will apply to the combination as such. + +### 14. Revised Versions of this License + +The Free Software Foundation may publish revised and/or new versions of the GNU +General Public License from time to time. Such new versions will be similar in spirit +to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that +a certain numbered version of the GNU General Public License “or any later +version” applies to it, you have the option of following the terms and +conditions either of that numbered version or of any later version published by the +Free Software Foundation. If the Program does not specify a version number of the GNU +General Public License, you may choose any version ever published by the Free +Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU +General Public License can be used, that proxy's public statement of acceptance of a +version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no +additional obligations are imposed on any author or copyright holder as a result of +your choosing to follow a later version. + +### 15. Disclaimer of Warranty + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE +QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +### 16. Limitation of Liability + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY +COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS +PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, +INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE +OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE +WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +### 17. Interpretation of Sections 15 and 16 + +If the disclaimer of warranty and limitation of liability provided above cannot be +given local legal effect according to their terms, reviewing courts shall apply local +law that most closely approximates an absolute waiver of all civil liability in +connection with the Program, unless a warranty or assumption of liability accompanies +a copy of the Program in return for a fee. + +_END OF TERMS AND CONDITIONS_ + +## How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to +the public, the best way to achieve this is to make it free software which everyone +can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them +to the start of each source file to most effectively state the exclusion of warranty; +and each file should have at least the “copyright” line and a pointer to +where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like this +when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type 'show c' for details. + +The hypothetical commands `show w` and `show c` should show the appropriate parts of +the General Public License. Of course, your program's commands might be different; +for a GUI interface, you would use an “about box”. + +You should also get your employer (if you work as a programmer) or school, if any, to +sign a “copyright disclaimer” for the program, if necessary. For more +information on this, and how to apply and follow the GNU GPL, see +<>. + +The GNU General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may consider it +more useful to permit linking proprietary applications with the library. If this is +what you want to do, use the GNU Lesser General Public License instead of this +License. But first, please read +<>. diff --git a/data/cosmic-mimeapps.list b/data/cosmic-mimeapps.list new file mode 100644 index 0000000..8637e9b --- /dev/null +++ b/data/cosmic-mimeapps.list @@ -0,0 +1,315 @@ +[Default Applications] +text/plain=com.system76.CosmicEdit.desktop +text/css=com.system76.CosmicEdit.desktop +text/javascript=com.system76.CosmicEdit.desktop +text/markdown=com.system76.CosmicEdit.desktop +text/mathml=com.system76.CosmicEdit.desktop +text/rust=com.system76.CosmicEdit.desktop +text/x-c++hdr=com.system76.CosmicEdit.desktop +text/x-c++src=com.system76.CosmicEdit.desktop +text/x-csrc=com.system76.CosmicEdit.desktop +text/x-chdr=com.system76.CosmicEdit.desktop +text/x-dtd=com.system76.CosmicEdit.desktop +text/x-java=com.system76.CosmicEdit.desktop +text/x-javascript=com.system76.CosmicEdit.desktop +text/x-makefile=com.system76.CosmicEdit.desktop +text/x-moc=com.system76.CosmicEdit.desktop +text/x-pascal=com.system76.CosmicEdit.desktop +text/x-patch=com.system76.CosmicEdit.desktop +text/x-perl=com.system76.CosmicEdit.desktop +text/x-php=com.system76.CosmicEdit.desktop +text/x-python=com.system76.CosmicEdit.desktop +text/x-sql=com.system76.CosmicEdit.desktop +text/x-tcl=com.system76.CosmicEdit.desktop +text/x-tex=com.system76.CosmicEdit.desktop +text/xml=com.system76.CosmicEdit.desktop +application/javascript=com.system76.CosmicEdit.desktop +application/x-cgi=com.system76.CosmicEdit.desktop +application/x-javascript=com.system76.CosmicEdit.desktop +application/x-perl=com.system76.CosmicEdit.desktop +application/x-php=com.system76.CosmicEdit.desktop +application/x-python=com.system76.CosmicEdit.desktop +application/x-shellscript=com.system76.CosmicEdit.desktop +application/xml=com.system76.CosmicEdit.desktop +application/xml-dtd=com.system76.CosmicEdit.desktop +inode/directory=com.system76.CosmicFiles.desktop +inode/mount-point=com.system76.CosmicFiles.desktop +application/mxf=com.system76.CosmicPlayer.desktop +application/ogg=com.system76.CosmicPlayer.desktop +application/ram=com.system76.CosmicPlayer.desktop +application/sdp=com.system76.CosmicPlayer.desktop +application/smil=com.system76.CosmicPlayer.desktop +application/smil+xml=com.system76.CosmicPlayer.desktop +application/vnd.ms-wpl=com.system76.CosmicPlayer.desktop +application/vnd.rn-realmedia=com.system76.CosmicPlayer.desktop +application/x-extension-m4a=com.system76.CosmicPlayer.desktop +application/x-extension-mp4=com.system76.CosmicPlayer.desktop +application/x-flac=com.system76.CosmicPlayer.desktop +application/x-flash-video=com.system76.CosmicPlayer.desktop +application/x-matroska=com.system76.CosmicPlayer.desktop +application/x-netshow-channel=com.system76.CosmicPlayer.desktop +application/x-ogg=com.system76.CosmicPlayer.desktop +application/x-quicktime-media-link=com.system76.CosmicPlayer.desktop +application/x-quicktimeplayer=com.system76.CosmicPlayer.desktop +application/x-shorten=com.system76.CosmicPlayer.desktop +application/x-smil=com.system76.CosmicPlayer.desktop +application/xspf+xml=com.system76.CosmicPlayer.desktop +audio/3gpp=com.system76.CosmicPlayer.desktop +audio/ac3=com.system76.CosmicPlayer.desktop +audio/AMR=com.system76.CosmicPlayer.desktop +audio/AMR-WB=com.system76.CosmicPlayer.desktop +audio/basic=com.system76.CosmicPlayer.desktop +audio/flac=com.system76.CosmicPlayer.desktop +audio/midi=com.system76.CosmicPlayer.desktop +audio/mp4=com.system76.CosmicPlayer.desktop +audio/mpeg=com.system76.CosmicPlayer.desktop +audio/mpegurl=com.system76.CosmicPlayer.desktop +audio/ogg=com.system76.CosmicPlayer.desktop +audio/prs.sid=com.system76.CosmicPlayer.desktop +audio/vnd.rn-realaudio=com.system76.CosmicPlayer.desktop +audio/x-ape=com.system76.CosmicPlayer.desktop +audio/x-flac=com.system76.CosmicPlayer.desktop +audio/x-gsm=com.system76.CosmicPlayer.desktop +audio/x-it=com.system76.CosmicPlayer.desktop +audio/x-m4a=com.system76.CosmicPlayer.desktop +audio/x-matroska=com.system76.CosmicPlayer.desktop +audio/x-mod=com.system76.CosmicPlayer.desktop +audio/x-mp3=com.system76.CosmicPlayer.desktop +audio/x-mpeg=com.system76.CosmicPlayer.desktop +audio/x-mpegurl=com.system76.CosmicPlayer.desktop +audio/x-ms-asf=com.system76.CosmicPlayer.desktop +audio/x-ms-asx=com.system76.CosmicPlayer.desktop +audio/x-ms-wax=com.system76.CosmicPlayer.desktop +audio/x-ms-wma=com.system76.CosmicPlayer.desktop +audio/x-musepack=com.system76.CosmicPlayer.desktop +audio/x-pn-aiff=com.system76.CosmicPlayer.desktop +audio/x-pn-au=com.system76.CosmicPlayer.desktop +audio/x-pn-realaudio=com.system76.CosmicPlayer.desktop +audio/x-pn-realaudio-plugin=com.system76.CosmicPlayer.desktop +audio/x-pn-wav=com.system76.CosmicPlayer.desktop +audio/x-pn-windows-acm=com.system76.CosmicPlayer.desktop +audio/x-realaudio=com.system76.CosmicPlayer.desktop +audio/x-real-audio=com.system76.CosmicPlayer.desktop +audio/x-sbc=com.system76.CosmicPlayer.desktop +audio/x-scpls=com.system76.CosmicPlayer.desktop +audio/x-speex=com.system76.CosmicPlayer.desktop +audio/x-tta=com.system76.CosmicPlayer.desktop +audio/x-vorbis=com.system76.CosmicPlayer.desktop +audio/x-vorbis+ogg=com.system76.CosmicPlayer.desktop +audio/x-wav=com.system76.CosmicPlayer.desktop +audio/x-wavpack=com.system76.CosmicPlayer.desktop +audio/x-xm=com.system76.CosmicPlayer.desktop +image/vnd.rn-realpix=com.system76.CosmicPlayer.desktop +image/x-pict=com.system76.CosmicPlayer.desktop +misc/ultravox=com.system76.CosmicPlayer.desktop +text/google-video-pointer=com.system76.CosmicPlayer.desktop +text/x-google-video-pointer=com.system76.CosmicPlayer.desktop +video/3gpp=com.system76.CosmicPlayer.desktop +video/dv=com.system76.CosmicPlayer.desktop +video/fli=com.system76.CosmicPlayer.desktop +video/flv=com.system76.CosmicPlayer.desktop +video/mp2t=com.system76.CosmicPlayer.desktop +video/mp4=com.system76.CosmicPlayer.desktop +video/mp4v-es=com.system76.CosmicPlayer.desktop +video/mpeg=com.system76.CosmicPlayer.desktop +video/msvideo=com.system76.CosmicPlayer.desktop +video/ogg=com.system76.CosmicPlayer.desktop +video/quicktime=com.system76.CosmicPlayer.desktop +video/vivo=com.system76.CosmicPlayer.desktop +video/vnd.divx=com.system76.CosmicPlayer.desktop +video/vnd.rn-realvideo=com.system76.CosmicPlayer.desktop +video/vnd.vivo=com.system76.CosmicPlayer.desktop +video/webm=com.system76.CosmicPlayer.desktop +video/x-anim=com.system76.CosmicPlayer.desktop +video/x-avi=com.system76.CosmicPlayer.desktop +video/x-flc=com.system76.CosmicPlayer.desktop +video/x-fli=com.system76.CosmicPlayer.desktop +video/x-flic=com.system76.CosmicPlayer.desktop +video/x-flv=com.system76.CosmicPlayer.desktop +video/x-m4v=com.system76.CosmicPlayer.desktop +video/x-matroska=com.system76.CosmicPlayer.desktop +video/x-mpeg=com.system76.CosmicPlayer.desktop +video/x-ms-asf=com.system76.CosmicPlayer.desktop +video/x-ms-asx=com.system76.CosmicPlayer.desktop +video/x-msvideo=com.system76.CosmicPlayer.desktop +video/x-ms-wm=com.system76.CosmicPlayer.desktop +video/x-ms-wmv=com.system76.CosmicPlayer.desktop +video/x-ms-wmx=com.system76.CosmicPlayer.desktop +video/x-ms-wvx=com.system76.CosmicPlayer.desktop +video/x-nsv=com.system76.CosmicPlayer.desktop +video/x-ogm+ogg=com.system76.CosmicPlayer.desktop +video/x-theora+ogg=com.system76.CosmicPlayer.desktop +video/x-totem-stream=com.system76.CosmicPlayer.desktop +x-content/video-dvd=com.system76.CosmicPlayer.desktop +x-content/video-vcd=com.system76.CosmicPlayer.desktop +x-content/video-svcd=com.system76.CosmicPlayer.desktop +x-scheme-handler/pnm=com.system76.CosmicPlayer.desktop +x-scheme-handler/mms=com.system76.CosmicPlayer.desktop +x-scheme-handler/net=com.system76.CosmicPlayer.desktop +x-scheme-handler/rtp=com.system76.CosmicPlayer.desktop +x-scheme-handler/rtsp=com.system76.CosmicPlayer.desktop +x-scheme-handler/mmsh=com.system76.CosmicPlayer.desktop +x-scheme-handler/uvox=com.system76.CosmicPlayer.desktop +x-scheme-handler/icy=com.system76.CosmicPlayer.desktop +x-scheme-handler/icyx=com.system76.CosmicPlayer.desktop +application/x-cd-image=com.system76.Popsicle.desktop +application/x-raw-disk-image=com.system76.Popsicle.desktop +application/x-raw-disk-image-xz-compressed=com.system76.Popsicle.desktop +application/x-debian-package=com.system76.CosmicStore.desktop +application/vnd.debian.binary-package=com.system76.CosmicStore.desktop +application/vnd.flatpak.ref=com.system76.CosmicStore.desktop +x-scheme-handler/appstream=com.system76.CosmicStore.desktop +x-scheme-handler/mime=com.system76.CosmicStore.desktop +image/bmp=org.gnome.eog.desktop +image/gif=org.gnome.eog.desktop +image/jpeg=org.gnome.eog.desktop +image/jpg=org.gnome.eog.desktop +image/pjpeg=org.gnome.eog.desktop +image/png=org.gnome.eog.desktop +image/svg+xml=org.gnome.eog.desktop +image/svg+xml-compressed=org.gnome.eog.desktop +image/x-bmp=org.gnome.eog.desktop +image/x-gray=org.gnome.eog.desktop +image/x-icb=org.gnome.eog.desktop +image/x-ico=org.gnome.eog.desktop +image/x-pcx=org.gnome.eog.desktop +image/x-png=org.gnome.eog.desktop +image/x-portable-anymap=org.gnome.eog.desktop +image/x-portable-bitmap=org.gnome.eog.desktop +image/x-portable-graymap=org.gnome.eog.desktop +image/x-portable-pixmap=org.gnome.eog.desktop +image/x-xbitmap=org.gnome.eog.desktop +image/x-xpixmap=org.gnome.eog.desktop +image/vnd.wap.wbmp=org.gnome.eog.desktop +image/g3fax=gimp.desktop +image/x-compressed-xcf=gimp.desktop +image/x-fits=gimp.desktop +image/x-icon=gimp.desktop +image/x-psd=gimp.desktop +image/x-sgi=gimp.desktop +image/x-sun-raster=gimp.desktop +image/x-tga=gimp.desktop +image/x-xcf=gimp.desktop +image/x-xwindowdump=gimp.desktop +application/pdf=org.gnome.Evince.desktop +application/x-bzpdf=org.gnome.Evince.desktop +application/x-gzpdf=org.gnome.Evince.desktop +application/postscript=org.gnome.Evince.desktop +application/x-bzpostscript=org.gnome.Evince.desktop +application/x-gzpostscript=org.gnome.Evince.desktop +image/x-eps=org.gnome.Evince.desktop +image/x-bzeps=org.gnome.Evince.desktop +image/x-gzeps=org.gnome.Evince.desktop +application/x-dvi=org.gnome.Evince.desktop +application/x-bzdvi=org.gnome.Evince.desktop +application/x-gzdvi=org.gnome.Evince.desktop +image/vnd.djvu=org.gnome.Evince.desktop +image/tiff=org.gnome.Evince.desktop +application/x-cbr=org.gnome.Evince.desktop +application/x-cbz=org.gnome.Evince.desktop +application/x-cb7=org.gnome.Evince.desktop +application/x-7z-compressed=org.gnome.FileRoller.desktop +application/x-7z-compressed-tar=org.gnome.FileRoller.desktop +application/x-ace=org.gnome.FileRoller.desktop +application/x-alz=org.gnome.FileRoller.desktop +application/x-ar=org.gnome.FileRoller.desktop +application/x-arj=org.gnome.FileRoller.desktop +application/x-bzip=org.gnome.FileRoller.desktop +application/x-bzip-compressed-tar=org.gnome.FileRoller.desktop +application/x-bzip1=org.gnome.FileRoller.desktop +application/x-bzip1-compressed-tar=org.gnome.FileRoller.desktop +application/x-cabinet=org.gnome.FileRoller.desktop +application/x-compress=org.gnome.FileRoller.desktop +application/x-compressed-tar=org.gnome.FileRoller.desktop +application/x-cpio=org.gnome.FileRoller.desktop +application/x-deb=org.gnome.FileRoller.desktop +application/x-ear=org.gnome.FileRoller.desktop +application/x-gtar=org.gnome.FileRoller.desktop +application/x-gzip=org.gnome.FileRoller.desktop +application/x-java-archive=org.gnome.FileRoller.desktop +application/x-lha=org.gnome.FileRoller.desktop +application/x-lhz=org.gnome.FileRoller.desktop +application/x-lzip=org.gnome.FileRoller.desktop +application/x-lzip-compressed-tar=org.gnome.FileRoller.desktop +application/x-lzma=org.gnome.FileRoller.desktop +application/x-lzma-compressed-tar=org.gnome.FileRoller.desktop +application/x-lzop=org.gnome.FileRoller.desktop +application/x-lzop-compressed-tar=org.gnome.FileRoller.desktop +application/x-rar=org.gnome.FileRoller.desktop +application/x-rar-compressed=org.gnome.FileRoller.desktop +application/x-rpm=org.gnome.FileRoller.desktop +application/x-rzip=org.gnome.FileRoller.desktop +application/x-tar=org.gnome.FileRoller.desktop +application/x-tarz=org.gnome.FileRoller.desktop +application/x-stuffit=org.gnome.FileRoller.desktop +application/x-war=org.gnome.FileRoller.desktop +application/x-xz=org.gnome.FileRoller.desktop +application/x-xz-compressed-tar=org.gnome.FileRoller.desktop +application/x-zip=org.gnome.FileRoller.desktop +application/x-zip-compressed=org.gnome.FileRoller.desktop +application/x-zoo=org.gnome.FileRoller.desktop +application/zip=org.gnome.FileRoller.desktop +multipart/x-zip=org.gnome.FileRoller.desktop +application/x-font-ttf=org.gnome.font-viewer.desktop +application/x-font-pcf=org.gnome.font-viewer.desktop +application/x-font-type1=org.gnome.font-viewer.desktop +application/x-font-otf=org.gnome.font-viewer.desktop +text/html=firefox-esr.desktop;firefox.desktop; +application/xhtml+xml=firefox-esr.desktop;firefox.desktop; +application/rss+xml=firefox-esr.desktop;firefox.desktop; +application/rdf+xml=firefox-esr.desktop;firefox.desktop; +x-scheme-handler/http=firefox-esr.desktop;firefox.desktop; +x-scheme-handler/https=firefox-esr.desktop;firefox.desktop; +application/vnd.oasis.opendocument.spreadsheet=libreoffice-calc.desktop +application/vnd.oasis.opendocument.spreadsheet-flat-xml=libreoffice-calc.desktop +application/vnd.oasis.opendocument.spreadsheet-template=libreoffice-calc.desktop +application/vnd.sun.xml.calc=libreoffice-calc.desktop +application/vnd.sun.xml.calc.template=libreoffice-calc.desktop +application/msexcel=libreoffice-calc.desktop +application/vnd.ms-excel=libreoffice-calc.desktop +application/vnd.openxmlformats-officedocument.spreadsheetml.sheet=libreoffice-calc.desktop +application/vnd.ms-excel.sheet.macroenabled.12=libreoffice-calc.desktop +application/vnd.openxmlformats-officedocument.spreadsheetml.template=libreoffice-calc.desktop +application/vnd.ms-excel.template.macroenabled.12=libreoffice-calc.desktop +application/vnd.ms-excel.sheet.binary.macroenabled.12=libreoffice-calc.desktop +application/x-dbf=libreoffice-calc.desktop +text/spreadsheet=libreoffice-calc.desktop +application/vnd.oasis.opendocument.graphics=libreoffice-draw.desktop +application/vnd.oasis.opendocument.graphics-flat-xml=libreoffice-draw.desktop +application/vnd.oasis.opendocument.graphics-template=libreoffice-draw.desktop +application/vnd.sun.xml.draw=libreoffice-draw.desktop +application/vnd.sun.xml.draw.template=libreoffice-draw.desktop +application/vnd.visio=libreoffice-draw.desktop +application/vnd.oasis.opendocument.presentation=libreoffice-impress.desktop +application/vnd.oasis.opendocument.presentation-flat-xml=libreoffice-impress.desktop +application/vnd.oasis.opendocument.presentation-template=libreoffice-impress.desktop +application/vnd.sun.xml.impress=libreoffice-impress.desktop +application/vnd.sun.xml.impress.template=libreoffice-impress.desktop +application/mspowerpoint=libreoffice-impress.desktop +application/vnd.ms-powerpoint=libreoffice-impress.desktop +application/vnd.openxmlformats-officedocument.presentationml.presentation=libreoffice-impress.desktop +application/vnd.ms-powerpoint.presentation.macroenabled.12=libreoffice-impress.desktop +application/vnd.openxmlformats-officedocument.presentationml.template=libreoffice-impress.desktop +application/vnd.ms-powerpoint.template.macroenabled.12=libreoffice-impress.desktop +application/vnd.openxmlformats-officedocument.presentationml.slide=libreoffice-impress.desktop +application/vnd.openxmlformats-officedocument.presentationml.slideshow=libreoffice-impress.desktop +application/vnd.oasis.opendocument.formula=libreoffice-math.desktop +application/vnd.sun.xml.math=libreoffice-math.desktop +application/vnd.oasis.opendocument.text=libreoffice-writer.desktop +application/vnd.oasis.opendocument.text-flat-xml=libreoffice-writer.desktop +application/vnd.oasis.opendocument.text-template=libreoffice-writer.desktop +application/vnd.oasis.opendocument.text-web=libreoffice-writer.desktop +application/vnd.oasis.opendocument.text-master=libreoffice-writer.desktop +application/vnd.sun.xml.writer=libreoffice-writer.desktop +application/vnd.sun.xml.writer.template=libreoffice-writer.desktop +application/vnd.sun.xml.writer.global=libreoffice-writer.desktop +eapplication/vnd.ms-word=libreoffice-writer.desktop +application/x-doc=libreoffice-writer.desktop +application/x-hwp=libreoffice-writer.desktop +application/vnd.wordperfect=libreoffice-writer.desktop +application/wordperfect=libreoffice-writer.desktop +application/vnd.lotus-wordpro=libreoffice-writer.desktop +application/vnd.openxmlformats-officedocument.wordprocessingml.document=libreoffice-writer.desktop +application/vnd.ms-word.document.macroenabled.12=libreoffice-writer.desktop +application/vnd.openxmlformats-officedocument.wordprocessingml.template=libreoffice-writer.desktop +application/vnd.ms-word.template.macroenabled.12=libreoffice-writer.desktop diff --git a/data/cosmic-session.target b/data/cosmic-session.target new file mode 100644 index 0000000..b459124 --- /dev/null +++ b/data/cosmic-session.target @@ -0,0 +1,12 @@ +[Unit] +Description=Cosmic Session Target +Documentation=man:systemd.special(7) + +BindsTo=graphical-session.target +Before=graphical-session.target + +Wants=graphical-session-pre.target +After=graphical-session-pre.target + +Wants=xdg-desktop-autostart.target +Before=xdg-desktop-autostart.target diff --git a/data/cosmic.desktop b/data/cosmic.desktop new file mode 100644 index 0000000..6b688c1 --- /dev/null +++ b/data/cosmic.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Name=COSMIC +Comment=This session logs you into the COSMIC desktop +Comment[sv]=Denna session loggar in dig till skrivbordsmiljön COSMIC +Exec=/usr/bin/start-cosmic +Type=Application +DesktopNames=COSMIC diff --git a/data/dconf/profile/cosmic b/data/dconf/profile/cosmic new file mode 100644 index 0000000..a290627 --- /dev/null +++ b/data/dconf/profile/cosmic @@ -0,0 +1,2 @@ +user-db:cosmic +user-db:user diff --git a/data/start-cosmic b/data/start-cosmic new file mode 100755 index 0000000..c91f463 --- /dev/null +++ b/data/start-cosmic @@ -0,0 +1,114 @@ +#!/bin/bash + +set -e + +# From: https://people.debian.org/~mpitt/systemd.conf-2016-graphical-session.pdf + +if command -v systemctl >/dev/null; then + # robustness: if the previous graphical session left some failed units, + # reset them so that they don't break this startup + for unit in $(systemctl --user --no-legend --state=failed --plain list-units | cut -f1 -d' '); do + partof="$(systemctl --user show -p PartOf --value "$unit")" + for target in cosmic-session.target graphical-session.target; do + if [ "$partof" = "$target" ]; then + systemctl --user reset-failed "$unit" + break + fi + done + done +fi + +# use the user's preferred shell to acquire environment variables +# see: https://github.com/pop-os/cosmic-session/issues/23 +if [ -n "${SHELL}" ]; then + # --in-login-shell: our flag to indicate that we don't need to recurse any further + if [ "${1}" != "--in-login-shell" ]; then + # `exec -l`: like `login`, prefixes $SHELL with a hyphen to start a login shell + exec bash -c "exec -l '${SHELL}' -c '${0} --in-login-shell'" + fi +fi + +export XDG_CURRENT_DESKTOP="${XDG_CURRENT_DESKTOP:=COSMIC}" +export XDG_SESSION_DESKTOP="${XDG_SESSION_DESKTOP:=COSMIC}" +export XDG_SESSION_TYPE="${XDG_SESSION_TYPE:=wayland}" +export _JAVA_AWT_WM_NONREPARENTING=1 +export GDK_BACKEND=wayland,x11 +export MOZ_ENABLE_WAYLAND=1 +export QT_QPA_PLATFORM="wayland;xcb" +export QT_AUTO_SCREEN_SCALE_FACTOR=1 +export QT_ENABLE_HIGHDPI_SCALING=1 +export DCONF_PROFILE=cosmic + +# Set the QT platform theme to CuteCosmic. Fallback to qt6ct if CuteCosmic is not installed. +if [ -z "$QT_QPA_PLATFORMTHEME" ]; then + export QT_QPA_PLATFORMTHEME=cosmic + for QT_PLUGIN_PATH in /usr/lib{*,/*}/qt6/plugins; do + if [ -f "${QT_PLUGIN_PATH}/platformthemes/libcutecosmictheme.so" ]; then + # CuteCosmic found, no need for a fallback. + export QT_QPA_PLATFORMTHEME=cosmic + break + elif [ -f "${QT_PLUGIN_PATH}/platformthemes/libqt6ct.so" ] || [ -f "${QT_PLUGIN_PATH}/platformthemes/libqt5ct.so" ]; then + # Fallback to qt6ct, but keep looking for CuteCosmic. + # Note that "qt5ct" is compatible with both qt5ct and qt6ct. + export QT_QPA_PLATFORMTHEME=qt5ct + fi + done +fi + +# Start gnome keyring components if the daemon is active +# -> check if /run/user/$UID/keyring exists +if [ -d "/run/user/$(id -u)/keyring" ]; then + + # Use PATH lookup instead of hardcoding /usr/bin + if command -v gnome-keyring-daemon >/dev/null 2>&1; then + eval "$(gnome-keyring-daemon --start --components=pkcs11,secrets,ssh > /dev/null 2>&1)" + else + echo "gnome-keyring-daemon not found in PATH" >&2 + fi + + # Only set SSH_AUTH_SOCK if the socket actually exists. Either + # set the correct one, or don't set one at all. Don't set the + # wrong value. + if [ -S "/run/user/$(id -u)/gcr/ssh" ]; then + export SSH_AUTH_SOCK="/run/user/$(id -u)/gcr/ssh" + elif [ -S "/run/user/$(id -u)/keyring/ssh" ]; then + export SSH_AUTH_SOCK="/run/user/$(id -u)/keyring/ssh" + fi +fi + +if command -v systemctl >/dev/null; then + # Import some variables that we explicitly want to have available + # in the user session. + systemctl --user import-environment XDG_SESSION_TYPE XDG_CURRENT_DESKTOP DCONF_PROFILE SSH_AUTH_SOCK + + # For environment variables already imported into the user's + # session, if the value imported differs from the value in this + # environment, update it. + mapfile -t existing_env_vars < <(systemctl --user show-environment) + for env_var in "${existing_env_vars[@]}"; do + env_var_name="${env_var%%=*}" + env_var_value=${!env_var_name:-} + + # Skip current iteration if the environment variable's value + # in the current envionment is unset. + if [[ -z "${env_var_value}" ]]; then + continue + fi + + env_var_val_str_to_compare="${env_var_name}=${env_var_value}" + env_var_val_str_to_compare_ansi_c_quoted="${env_var_name}=\$'$(printf '%q' "${env_var_value}")'" + if [[ "${env_var}" == "${env_var_val_str_to_compare}" ]]; then + continue + elif [[ "${env_var}" == "${env_var_val_str_to_compare_ansi_c_quoted}" ]]; then + continue + fi + systemctl --user import-environment "${env_var_name}" ||: + done +fi + +# Run cosmic-session +if [[ -z "${DBUS_SESSION_BUS_ADDRESS}" ]]; then + exec /usr/bin/dbus-run-session -- /usr/bin/cosmic-session +else + exec /usr/bin/cosmic-session +fi diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..e890468 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +cosmic-session (1.0.0) noble; urgency=medium + + * Initial release + + -- Michael Murphy Thu, 11 Dec 2025 13:49:55 +0100 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..e8d41e0 --- /dev/null +++ b/debian/control @@ -0,0 +1,52 @@ +Source: cosmic-session +Section: admin +Priority: optional +Maintainer: System76 +Build-Depends: + debhelper (>= 11), + debhelper-compat (= 11), + cargo, + just +Standards-Version: 4.3.0 +Homepage: https://github.com/pop-os/cosmic-session + +Package: cosmic-session +Architecture: amd64 arm64 +Conflicts: seatd +Depends: + ${misc:Depends}, + ${shlibs:Depends}, + cosmic-app-library, + cosmic-applets, + cosmic-bg, + cosmic-comp, + cosmic-files, + cosmic-greeter, + cosmic-icons, + cosmic-idle, + cosmic-launcher, + cosmic-notifications, + cosmic-osd, + cosmic-panel, + cosmic-randr, + cosmic-screenshot, + cosmic-settings, + cosmic-settings-daemon, + cosmic-workspaces, + fonts-open-sans, + gnome-keyring, + libsecret-1-0, + pop-fonts, + switcheroo-control, + xdg-desktop-portal-cosmic, + xwayland, +Recommends: + cosmic-edit, + cosmic-monitor, + cosmic-player, + cosmic-store, + cosmic-term, + cosmic-wallpapers, + orca, + system-config-printer, +Description: The session for the COSMIC desktop diff --git a/debian/cosmic-session.gsettings-override b/debian/cosmic-session.gsettings-override new file mode 100644 index 0000000..0de95c2 --- /dev/null +++ b/debian/cosmic-session.gsettings-override @@ -0,0 +1,8 @@ +[org.gnome.desktop.interface:COSMIC] +color-scheme = "prefer-dark" +gtk-theme = "Pop-dark" +icon-theme = "Pop" +cursor-theme = "Pop" +font-name = "Open Sans 11" +document-font-name = "Open Sans 11" +monospace-font-name = "Noto Sans Mono 11" diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..1d07f98 --- /dev/null +++ b/debian/rules @@ -0,0 +1,15 @@ +#!/usr/bin/make -f + +export DESTDIR = debian/cosmic-session + +%: + dh $@ + +override_dh_auto_clean: + ischroot || just vendor + +override_dh_auto_build: + test -e vendor.tar && just build-vendored || just + +override_dh_auto_install: + just rootdir=$(DESTDIR) install diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..8d2b776 --- /dev/null +++ b/flake.lock @@ -0,0 +1,137 @@ +{ + "nodes": { + "crane": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1702141249, + "narHash": "sha256-8wDpJKbDTDqFmyJfNEJOLrHYDoEzCjCbmz+lSRoU3CI=", + "owner": "ipetkov", + "repo": "crane", + "rev": "62fc1a0cbe144c1014d956e603d56bf1ffe69c7d", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1702189261, + "narHash": "sha256-TN6gE1eZddDhAoRrScV6Wji1Nk3uqMIDjGwN5ZesAZk=", + "owner": "nix-community", + "repo": "fenix", + "rev": "cae060dbaf53430bb2b549ced0affd54d40e6cee", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nix-filter": { + "locked": { + "lastModified": 1701697642, + "narHash": "sha256-L217WytWZHSY8GW9Gx1A64OnNctbuDbfslaTEofXXRw=", + "owner": "numtide", + "repo": "nix-filter", + "rev": "c843418ecfd0344ecb85844b082ff5675e02c443", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "nix-filter", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1702206697, + "narHash": "sha256-vE9oEx3Y8TO5MnWwFlmopjHd1JoEBno+EhsfUCq5iR8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "29d6c96900b9b576c2fb89491452f283aa979819", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "crane": "crane", + "fenix": "fenix", + "flake-utils": "flake-utils", + "nix-filter": "nix-filter", + "nixpkgs": "nixpkgs" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1702153490, + "narHash": "sha256-F98s0+mUHtqiUk9iCApPTy23YMLmcKqTfsShpIPB40Q=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "9d87a23cdef6087c1a0c97980949e2310271a941", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..eb7660f --- /dev/null +++ b/flake.nix @@ -0,0 +1,81 @@ +{ + description = "Session manager for the COSMIC desktop environment"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + nix-filter.url = "github:numtide/nix-filter"; + crane = { + url = "github:ipetkov/crane"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { self, nixpkgs, flake-utils, nix-filter, crane, fenix }: + flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" ] (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + craneLib = crane.lib.${system}.overrideToolchain fenix.packages.${system}.stable.toolchain; + + pkgDef = { + nativeBuildInputs = with pkgs; [ just pkg-config autoPatchelfHook ]; + buildInputs = with pkgs; [ + stdenv.cc.cc.lib + ]; + src = nix-filter.lib.filter { + root = ./.; + include = [ + ./src + ./Cargo.toml + ./Cargo.lock + ./Justfile + ./data + ]; + }; + }; + + cargoArtifacts = craneLib.buildDepsOnly pkgDef; + cosmic-session = craneLib.buildPackage (pkgDef // { + inherit cargoArtifacts; + }); + in { + checks = { + inherit cosmic-session; + }; + + packages.default = cosmic-session.overrideAttrs (oldAttrs: rec { + buildPhase = '' + just prefix=$out xdp_cosmic=/run/current-system/sw/bin/xdg-desktop-portal-cosmic build + ''; + installPhase = '' + runHook preInstallPhase + just prefix=$out install + ''; + preInstallPhase = '' + substituteInPlace data/start-cosmic --replace '#!/bin/bash' "#!${pkgs.bash}/bin/bash" + substituteInPlace data/start-cosmic --replace '/usr/bin/cosmic-session' "${placeholder "out"}/bin/cosmic-session" + substituteInPlace data/start-cosmic --replace '/usr/bin/dbus-run-session' "${pkgs.dbus}/bin/dbus-run-session" + substituteInPlace data/cosmic.desktop --replace '/usr/bin/start-cosmic' "${placeholder "out"}/bin/start-cosmic" + ''; + passthru.providedSessions = [ "cosmic" ]; + }); + + apps.default = flake-utils.lib.mkApp { + drv = cosmic-session; + }; + + devShells.default = pkgs.mkShell { + inputsFrom = builtins.attrValues self.checks.${system}; + }; + }); + + nixConfig = { + # Cache for the Rust toolchain in fenix + extra-substituters = [ "https://nix-community.cachix.org" ]; + extra-trusted-public-keys = [ "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" ]; + }; +} diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..cac8fe8 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,10 @@ +edition = "2024" +hard_tabs = true +use_field_init_shorthand = true +# Unstable formatting options below; remove if you REALLY don't wanna use `cargo +nightly fmt` +format_code_in_doc_comments = true +format_strings = true +imports_granularity = "Module" +normalize_comments = true +reorder_impl_items = true +wrap_comments = true diff --git a/src/a11y.rs b/src/a11y.rs new file mode 100644 index 0000000..6c30191 --- /dev/null +++ b/src/a11y.rs @@ -0,0 +1,80 @@ +use futures_util::StreamExt; +use launch_pad::ProcessManager; +use tokio::sync::mpsc; +use tracing::Instrument; + +const ORCA: Option<&'static str> = option_env!("ORCA"); + +pub async fn start_a11y( + env_vars: Vec<(String, String)>, + pman: ProcessManager, +) -> color_eyre::Result<()> { + let (tx, mut rx) = mpsc::unbounded_channel(); + let mut process_key = None; + let conn = zbus::Connection::session().await?; + let proxy = cosmic_dbus_a11y::StatusProxy::new(&conn).await?; + + tokio::spawn(async move { + let mut watch_changes = proxy.receive_screen_reader_enabled_changed().await; + let mut enabled = false; + if let Ok(status) = proxy.screen_reader_enabled().await { + _ = tx.send(status); + + enabled = status; + } + while let Some(change) = watch_changes.next().await { + let Ok(new_enabled) = change.get().await else { + tokio::time::sleep(tokio::time::Duration::from_secs(10)).await; + continue; + }; + if enabled != new_enabled { + _ = tx.send(new_enabled); + enabled = new_enabled; + } + } + }); + + while let Some(enabled) = rx.recv().await { + let stdout_span = info_span!(parent: None, "screen-reader"); + let stderr_span = stdout_span.clone(); + if enabled && process_key.is_none() { + // spawn orca + match pman + .start( + launch_pad::process::Process::new() + .with_executable(ORCA.unwrap_or("/usr/bin/orca")) + .with_env(env_vars.clone()) + .with_on_stdout(move |_, _, line| { + let stdout_span = stdout_span.clone(); + async move { + info!("{}", line); + } + .instrument(stdout_span) + }) + .with_on_stderr(move |_, _, line| { + let stderr_span = stderr_span.clone(); + async move { + warn!("{}", line); + } + .instrument(stderr_span) + }), + ) + .await + { + Ok(key) => { + process_key = Some(key); + } + Err(err) => { + tracing::error!("Failed to start screen reader {err:?}"); + } + } + } else if !enabled && process_key.is_some() { + // kill orca + info!("Stopping screen reader"); + if let Err(err) = pman.stop_process(process_key.take().unwrap()).await { + tracing::error!("Failed to stop screen reader. {err:?}") + } + } + } + Ok(()) +} diff --git a/src/comp.rs b/src/comp.rs new file mode 100644 index 0000000..8457d08 --- /dev/null +++ b/src/comp.rs @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-3.0-only +use color_eyre::eyre::{Result, WrapErr}; +use launch_pad::ProcessManager; +use launch_pad::process::Process; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::os::unix::prelude::*; +use tokio::io::AsyncReadExt; +use tokio::net::UnixStream; +use tokio::net::unix::OwnedReadHalf; +use tokio::sync::{mpsc, oneshot}; +use tokio::task::JoinHandle; +use tokio_util::sync::CancellationToken; + +use crate::process::mark_as_not_cloexec; +use crate::service::SessionRequest; + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "snake_case", tag = "message")] +pub enum Message { + SetEnv { variables: HashMap }, +} + +// Cancellation safe! +#[derive(Default)] +struct IpcState { + env_tx: Option>>, + length: Option, + bytes_read: usize, + buf: Vec, +} + +fn parse_and_handle_ipc(state: &mut IpcState) { + match serde_json::from_slice::(&state.buf) { + Ok(Message::SetEnv { variables }) => { + if let Some(env_tx) = state.env_tx.take() { + env_tx.send(variables).unwrap(); + } + } + Err(_) => { + warn!( + "Unknown session socket message, are you using incompatible cosmic-session and \ + cosmic-comp versions?" + ) + } + } +} + +async fn receive_ipc(state: &mut IpcState, rx: &mut OwnedReadHalf) -> Result<()> { + // This is kind of a doozy, but this is kinda complex so it can be + // cancellation-safe. + match state.length { + // We already got the length, and are currently reading the message body. + Some(length) => { + let index = state.bytes_read.saturating_sub(1); + // Add the amount of bytes read to our state. + // I don't think this is entirely cancellation safe, which worries me. + state.bytes_read += rx + .read_exact(&mut state.buf[index..]) + .await + .wrap_err("failed to read IPC length")?; + // If we've read enough bytes, parse the message. + if state.bytes_read >= length as usize { + parse_and_handle_ipc(state); + // Set the state back to the default "waiting for a length" mode. + state.length = None; + state.bytes_read = 0; + state.buf.clear(); + } + Ok(()) + } + None => { + // Resize the state buffer enough to fit a u16./ + state.buf.resize(2, 0); + let index = state.bytes_read.saturating_sub(1); + // Read the remaining bytes of the length. + state.bytes_read += rx + .read_exact(&mut state.buf[index..]) + .await + .wrap_err("failed to read IPC length")?; + // If we've read two bytes, then parse a native-endian u16 from them. + if state.bytes_read >= 2 { + let length = u16::from_ne_bytes( + state.buf[..2] + .try_into() + .wrap_err("failed to convert IPC length to u16")?, + ); + // Set the state to "reading the message body" mode, as we now have the length. + state.length = Some(length); + state.bytes_read = 0; + state.buf.resize(length as usize, 0); + } + Ok(()) + } + } +} + +pub fn run_compositor( + process_manager: &ProcessManager, + exec: String, + args: Vec, + _token: CancellationToken, + env_tx: oneshot::Sender>, + session_dbus_tx: mpsc::Sender, +) -> Result>> { + let process_manager = process_manager.clone(); + // Create a pair of unix sockets - one for us (session), + // one for the compositor (comp) + let (session, comp) = UnixStream::pair().wrap_err("failed to create pair of unix sockets")?; + let (mut session_rx, _session_tx) = session.into_split(); + // Convert our compositor socket to a non-blocking file descriptor. + let comp = { + let std_stream = comp + .into_std() + .wrap_err("failed to convert compositor unix stream to a standard unix stream")?; + std_stream + .set_nonblocking(false) + .wrap_err("failed to mark compositor unix stream as blocking")?; + OwnedFd::from(std_stream) + }; + mark_as_not_cloexec(&comp).expect("Failed to mark fd as not cloexec"); + Ok(tokio::spawn(async move { + // Create a new process handler for cosmic-comp, with our compositor socket's + // file descriptor as the `COSMIC_SESSION_SOCK` environment variable. + process_manager + .start_process( + Process::new() + .with_executable(exec) + .with_args(args) + .with_env([("COSMIC_SESSION_SOCK", comp.as_raw_fd().to_string())]) + .with_on_exit(move |pman, _, err_code, _will_restart| { + let session_dbus_tx = session_dbus_tx.clone(); + async move { + pman.stop(); + if err_code == Some(0) { + info!("cosmic-comp exited successfully"); + session_dbus_tx.send(SessionRequest::Exit).await.unwrap(); + } else if let Some(err_code) = err_code { + error!("cosmic-comp exited with error code {}", err_code); + session_dbus_tx.send(SessionRequest::Restart).await.unwrap(); + } else { + warn!("cosmic-comp exited by signal"); + session_dbus_tx.send(SessionRequest::Restart).await.unwrap(); + } + } + }), + ) + .await + .expect("failed to launch compositor"); + // Create a new state object for IPC purposes. + let mut ipc_state = IpcState { + env_tx: Some(env_tx), + ..IpcState::default() + }; + loop { + tokio::select! { + /* + exit = receive_event(&mut rx) => if exit.is_none() { + break; + }, + */ + // Receive IPC messages from the process, + // exiting the loop if IPC errors. + result = receive_ipc(&mut ipc_state, &mut session_rx) => if let Err(err) = result { + error!("failed to receive IPC: {:?}", err); + break; + }, + } + } + Result::<()>::Ok(()) + })) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..0f2c00f --- /dev/null +++ b/src/main.rs @@ -0,0 +1,560 @@ +// SPDX-License-Identifier: GPL-3.0-only +#[macro_use] +extern crate tracing; + +mod a11y; +mod comp; +mod notifications; +mod process; +mod service; +mod systemd; + +use color_eyre::Result; +use color_eyre::eyre::WrapErr; +use launch_pad::ProcessManager; +use launch_pad::process::Process; +use service::SessionRequest; +use std::borrow::Cow; +#[cfg(feature = "autostart")] +use std::collections::HashSet; +use std::env; +use std::os::fd::AsRawFd; +#[cfg(feature = "autostart")] +use std::path::PathBuf; +#[cfg(feature = "autostart")] +use std::process::{Command, Stdio}; +use std::sync::Arc; +#[cfg(feature = "systemd")] +use systemd::{get_systemd_env, is_systemd_used, spawn_scope}; +use tokio::signal::unix::{SignalKind, signal}; +use tokio::sync::mpsc::{Receiver, Sender}; +use tokio::sync::{Mutex, oneshot}; +use tokio::time::Duration; +use tokio_util::sync::CancellationToken; +use tracing::Instrument; +use tracing::metadata::LevelFilter; +use tracing_subscriber::prelude::*; +use tracing_subscriber::{EnvFilter, fmt}; + +use crate::notifications::{ + DAEMON_NOTIFICATIONS_FD, PANEL_NOTIFICATIONS_FD, notifications_process, +}; +#[cfg(feature = "autostart")] +const AUTOSTART_DIR: &'static str = "autostart"; +#[cfg(feature = "autostart")] +const ENVIRONMENT_NAME: &'static str = "COSMIC"; + +#[tokio::main(flavor = "current_thread")] +async fn main() -> Result<()> { + color_eyre::install().wrap_err("failed to install color_eyre error handler")?; + + let trace = tracing_subscriber::registry(); + let env_filter = EnvFilter::builder() + .with_default_directive(LevelFilter::INFO.into()) + .from_env_lossy(); + + #[cfg(feature = "systemd")] + if let Ok(journald) = tracing_journald::layer() { + trace + .with(journald) + .with(env_filter) + .try_init() + .wrap_err("failed to initialize logger")?; + } else { + trace + .with(fmt::layer()) + .with(env_filter) + .try_init() + .wrap_err("failed to initialize logger")?; + warn!("failed to connect to journald") + } + + #[cfg(not(feature = "systemd"))] + trace + .with(fmt::layer()) + .with(env_filter) + .try_init() + .wrap_err("failed to initialize logger")?; + + log_panics::init(); + + let (session_tx, mut session_rx) = tokio::sync::mpsc::channel(10); + let session_tx_clone = session_tx.clone(); + let _conn = zbus::connection::Builder::session()? + .name("com.system76.CosmicSession")? + .serve_at( + "/com/system76/CosmicSession", + service::SessionService { session_tx }, + )? + .build() + .await?; + + loop { + match start(session_tx_clone.clone(), &mut session_rx).await { + Ok(Status::Exited) => { + info!("Exited cleanly"); + break; + } + Ok(Status::Restarted) => { + info!("Restarting"); + } + Err(error) => { + error!("Restarting after error: {:?}", error); + } + }; + // Drain the session channel. + while session_rx.try_recv().is_ok() {} + } + Ok(()) +} + +#[derive(Debug)] +pub enum Status { + Restarted, + Exited, +} + +async fn start( + session_tx: Sender, + session_rx: &mut Receiver, +) -> Result { + info!("Starting cosmic-session"); + + let mut args = env::args().skip(1); + let (executable, args) = ( + args.next().unwrap_or_else(|| String::from("cosmic-comp")), + args.collect::>(), + ); + + let process_manager = ProcessManager::new().await; + _ = process_manager.set_max_restarts(usize::MAX).await; + _ = process_manager + .set_restart_mode(launch_pad::RestartMode::ExponentialBackoff( + Duration::from_millis(10), + )) + .await; + let token = CancellationToken::new(); + let (env_tx, env_rx) = oneshot::channel(); + let compositor_handle = comp::run_compositor( + &process_manager, + executable.clone(), + args, + token.child_token(), + env_tx, + session_tx, + ) + .wrap_err("failed to start compositor")?; + + let mut env_vars = env_rx + .await + .expect("failed to receive environmental variables") + .into_iter() + .collect::>(); + info!( + "got environmental variables from cosmic-comp: {:?}", + env_vars + ); + + // now that cosmic-comp is ready, set XDG_SESSION_TYPE=wayland for new processes + env_vars.push(("XDG_SESSION_TYPE".to_string(), "wayland".to_string())); + systemd::set_systemd_environment("XDG_SESSION_TYPE", "wayland").await; + + #[cfg(feature = "systemd")] + let _inhibit_fd = if *is_systemd_used() { + match get_systemd_env().await { + Ok(env) => { + for systemd_env in env { + // Only update the envvar if unset + if std::env::var_os(&systemd_env.key).is_none() { + // Blacklist of envvars that we shouldn't touch (taken from KDE) + if (!systemd_env.key.starts_with("XDG_") + || systemd_env.key == "XDG_DATA_DIRS" + || systemd_env.key == "XDG_CONFIG_DIRS") + && systemd_env.key != "DISPLAY" + && systemd_env.key != "XAUTHORITY" + && systemd_env.key != "WAYLAND_DISPLAY" + && systemd_env.key != "WAYLAND_SOCKET" + && systemd_env.key != "_" + && systemd_env.key != "SHELL" + && systemd_env.key != "SHLVL" + { + env_vars.push((systemd_env.key, systemd_env.value)); + } + } + } + } + Err(err) => { + warn!("Failed to sync systemd environment {}.", err); + } + }; + #[cfg(feature = "logind")] + match zbus::Connection::system().await { + Ok(connection) => match logind_zbus::manager::ManagerProxy::new(&connection).await { + Ok(proxy) => match proxy + .inhibit( + logind_zbus::manager::InhibitType::HandlePowerKey, + "Cosmic Session", + "Show confirmation dialog.", + "block", + ) + .await + { + Ok(fd) => Some(fd), + Err(err) => { + error!("Failed to inhibit power key {err:?}"); + None + } + }, + Err(err) => { + error!("Failed to connect to logind manager {err:?}"); + None + } + }, + Err(err) => { + error!("Failed to connect to system dbus {err:?}"); + None + } + } + #[cfg(not(feature = "logind"))] + None + } else { + None + }; + + let stdout_span = info_span!(parent: None, "cosmic-settings-daemon"); + let stderr_span = stdout_span.clone(); + let (settings_exit_tx, settings_exit_rx) = oneshot::channel(); + let settings_exit_tx = Arc::new(std::sync::Mutex::new(Some(settings_exit_tx))); + let settings_daemon = process_manager + .start( + Process::new() + .with_executable("cosmic-settings-daemon") + .with_env(env_vars.iter().cloned()) + .with_on_stdout(move |_, _, line| { + let stdout_span = stdout_span.clone(); + async move { + info!("{}", line); + } + .instrument(stdout_span) + }) + .with_on_stderr(move |_, _, line| { + let stderr_span = stderr_span.clone(); + async move { + warn!("{}", line); + } + .instrument(stderr_span) + }) + .with_on_exit(move |_, _, _, will_restart| { + if !will_restart && let Some(tx) = settings_exit_tx.lock().unwrap().take() { + _ = tx.send(()); + } + async {} + }), + ) + .await + .expect("failed to start settings daemon"); + + // notifying the user service manager that we've reached the + // graphical-session.target, which should only happen after: + // - cosmic-comp is ready + // - we've set any related variables + // - cosmic-settings-daemon is ready + systemd::start_systemd_target().await; + // Always stop the target when the process exits or panics. + scopeguard::defer! { + systemd::stop_systemd_target(); + } + + // start a11y if configured + tokio::spawn(a11y::start_a11y(env_vars.clone(), process_manager.clone())); + + let (panel_notifications_fd, daemon_notifications_fd) = + notifications::create_socket().expect("Failed to create notification socket"); + + let mut daemon_env_vars = env_vars.clone(); + daemon_env_vars.push(( + DAEMON_NOTIFICATIONS_FD.to_string(), + daemon_notifications_fd.as_raw_fd().to_string(), + )); + let mut panel_env_vars = env_vars.clone(); + panel_env_vars.push(( + PANEL_NOTIFICATIONS_FD.to_string(), + panel_notifications_fd.as_raw_fd().to_string(), + )); + + let panel_key = Arc::new(Mutex::new(None)); + let notif_key = Arc::new(Mutex::new(None)); + + let notifications_span = info_span!(parent: None, "cosmic-notifications"); + let panel_span = info_span!(parent: None, "cosmic-panel"); + + let mut guard = notif_key.lock().await; + *guard = Some( + process_manager + .start(notifications_process( + notifications_span.clone(), + "cosmic-notifications", + notif_key.clone(), + daemon_env_vars.clone(), + daemon_notifications_fd, + panel_span.clone(), + "cosmic-panel", + panel_key.clone(), + panel_env_vars.clone(), + )) + .await + .expect("failed to start notifications daemon"), + ); + drop(guard); + + let mut guard = panel_key.lock().await; + *guard = Some( + process_manager + .start(notifications_process( + panel_span, + "cosmic-panel", + panel_key.clone(), + panel_env_vars, + panel_notifications_fd, + notifications_span, + "cosmic-notifications", + notif_key, + daemon_env_vars, + )) + .await + .expect("failed to start panel"), + ); + drop(guard); + + let span = info_span!(parent: None, "cosmic-app-library"); + start_component("cosmic-app-library", span, &process_manager, &env_vars).await; + + let span = info_span!(parent: None, "cosmic-launcher"); + start_component("cosmic-launcher", span, &process_manager, &env_vars).await; + + let span = info_span!(parent: None, "cosmic-workspaces"); + start_component("cosmic-workspaces", span, &process_manager, &env_vars).await; + + let span = info_span!(parent: None, "cosmic-osd"); + start_component("cosmic-osd", span, &process_manager, &env_vars).await; + + let span = info_span!(parent: None, "cosmic-bg"); + start_component("cosmic-bg", span, &process_manager, &env_vars).await; + + let span = info_span!(parent: None, "cosmic-greeter"); + start_component("cosmic-greeter", span, &process_manager, &env_vars).await; + + let span = info_span!(parent: None, "cosmic-files-applet"); + start_component("cosmic-files-applet", span, &process_manager, &env_vars).await; + + let span = info_span!(parent: None, "cosmic-idle"); + start_component("cosmic-idle", span, &process_manager, &env_vars).await; + + #[cfg(feature = "autostart")] + if !*is_systemd_used() { + info!("looking for autostart folders"); + let mut directories_to_scan = Vec::new(); + + // we start by taking user specific directories, so that we can deduplicate and + // ensure user overrides are respected + + // user specific directories + if let Some(user_config_dir) = dirs::config_dir() { + directories_to_scan.push(user_config_dir.join(AUTOSTART_DIR)); + } + + // system-wide directories + if let Some(xdg_config_dirs) = env::var_os("XDG_CONFIG_DIRS") { + let xdg_config_dirs = xdg_config_dirs + .into_string() + .expect("Invalid XDG_CONFIG_DIRS"); + let dir_list = xdg_config_dirs.split(":"); + + for dir in dir_list { + directories_to_scan.push(PathBuf::from(dir).join(AUTOSTART_DIR)); + } + } else { + directories_to_scan.push(PathBuf::from("/etc/xdg/").join(AUTOSTART_DIR)); + } + + info!("found autostart folders: {:?}", directories_to_scan); + + let mut dedupe = HashSet::new(); + + let iter = freedesktop_desktop_entry::Iter::new(directories_to_scan.into_iter()); + let autostart_env = env_vars.clone(); + for entry in iter.entries::<&str>(None) { + // we've already tried to execute this! + if dedupe.contains(&entry.appid) { + continue; + } + + // skip if we have an OnlyShowIn entry that doesn't include COSMIC + if let Some(only_show_in) = entry.only_show_in() { + if !only_show_in.contains(&ENVIRONMENT_NAME) { + continue; + } + } + + // ... OR we have a NotShowIn entry that includes COSMIC + if let Some(not_show_in) = entry.not_show_in() { + if not_show_in.contains(&ENVIRONMENT_NAME) { + continue; + } + } + + info!( + "trying to start appid {} ({})", + entry.appid, + entry.path.display() + ); + + if let Some(exec_raw) = entry.exec() { + let mut exec_words = exec_raw.split(" "); + + if let Some(program_name) = exec_words.next() { + // filter out any placeholder args, since we might not be able to deal with them + let filtered_args = exec_words + .filter(|s| !s.starts_with("%")) + .collect::>(); + + // escape them + let escaped_args = shell_words::split(&*filtered_args.join(" ")); + if let Ok(args) = escaped_args { + info!("trying to start {} {}", program_name, args.join(" ")); + + let mut command = Command::new(program_name); + command.args(args); + + // add relevant envs + for (k, v) in &autostart_env { + command.env(k, v); + } + + // detach stdin/out/err (should we?) + let child = command + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn(); + + if let Ok(child) = child { + info!( + "successfully started program {} {}", + entry.appid, + child.id() + ); + dedupe.insert(entry.appid); + } else { + info!("could not start program {}", entry.appid); + } + } else { + let why = escaped_args.unwrap_err(); + error!(?why, "could not parse arguments"); + } + } + } + } + info!("started {} programs", dedupe.len()); + } + + let mut sigterm = signal(SignalKind::terminate()).expect("Failed to bind SIGTERM handler"); + let mut sigint = signal(SignalKind::interrupt()).expect("Failed to bind SIGINT handler"); + let mut status = Status::Exited; + let session_dbus_rx_next = session_rx.recv(); + tokio::select! { + res = session_dbus_rx_next => { + match res { + Some(service::SessionRequest::Exit) => { + info!("EXITING: session exited by request"); + } + Some(service::SessionRequest::Restart) => { + info!("RESTARTING: session restarted by request"); + status = Status::Restarted; + } + None => { + warn!("exit channel dropped session"); + } + } + }, + _ = sigterm.recv() => { + info!("EXITING: received SIGTERM request to terminate"); + }, + _ = sigint.recv() => { + info!("EXITING: received SIGINT request to terminate"); + } + } + + compositor_handle.abort(); + token.cancel(); + if let Err(err) = process_manager.stop_process(settings_daemon).await { + tracing::error!(?err, "Failed to gracefully stop settings daemon."); + } else { + match tokio::time::timeout(Duration::from_secs(1), settings_exit_rx).await { + Ok(Ok(_)) => {} + _ => { + tracing::error!("Settings daemon process did not respond to the request to stop."); + } + }; + }; + + tokio::time::sleep(std::time::Duration::from_secs(2)).await; + Ok(status) +} + +async fn start_component( + cmd: impl Into>, + span: tracing::Span, + process_manager: &ProcessManager, + env_vars: &[(String, String)], +) { + let stdout_span = span.clone(); + let stderr_span = span.clone(); + let stderr_span_clone = stderr_span.clone(); + let cmd = cmd.into(); + let cmd_clone = cmd.clone(); + + if let Err(err) = process_manager + .start( + Process::new() + .with_executable(cmd.clone()) + .with_env(env_vars.iter().cloned()) + .with_on_stdout(move |_, _, line| { + let stdout_span = stdout_span.clone(); + async move { + info!("{}", line); + } + .instrument(stdout_span) + }) + .with_on_stderr(move |_, _, line| { + let stderr_span = stderr_span.clone(); + async move { + warn!("{}", line); + } + .instrument(stderr_span) + }) + .with_on_start(move |pman, pkey, _will_restart| async move { + #[cfg(feature = "systemd")] + if *is_systemd_used() + && let Ok((innr_cmd, Some(pid))) = pman.get_exe_and_pid(pkey).await + && let Err(err) = spawn_scope(innr_cmd.clone(), vec![pid]).await + { + warn!( + "Failed to spawn scope for {}. Creating transient unit failed with {}", + innr_cmd, err + ); + }; + }) + .with_on_exit(move |mut _pman, _key, err_code, _will_restart| { + if let Some(err) = err_code { + error!("{cmd_clone} exited with error {}", err.to_string()); + } + async {} + }), + ) + .await + { + let _enter = stderr_span_clone.enter(); + error!("failed to start {}: {}", cmd, err); + } +} diff --git a/src/notifications.rs b/src/notifications.rs new file mode 100644 index 0000000..3ac80f6 --- /dev/null +++ b/src/notifications.rs @@ -0,0 +1,126 @@ +use color_eyre::Result; +use color_eyre::eyre::Context; +use launch_pad::ProcessKey; +use launch_pad::process::Process; +use rustix::fd::AsRawFd; +use std::os::fd::OwnedFd; +use std::os::unix::net::UnixStream; +use std::sync::Arc; +use tokio::sync::Mutex; +use tracing::Instrument; + +pub const PANEL_NOTIFICATIONS_FD: &str = "PANEL_NOTIFICATIONS_FD"; +pub const DAEMON_NOTIFICATIONS_FD: &str = "DAEMON_NOTIFICATIONS_FD"; + +pub fn create_socket() -> Result<(OwnedFd, OwnedFd)> { + // Create a new pair of unnamed Unix sockets + let (sock_1, sock_2) = UnixStream::pair().wrap_err("failed to create socket pair")?; + + // Turn the sockets into non-blocking fd, which we can pass to the child + // process + sock_1 + .set_nonblocking(true) + .wrap_err("failed to mark client socket as non-blocking")?; + + sock_2 + .set_nonblocking(true) + .wrap_err("failed to mark client socket as non-blocking")?; + + Ok((OwnedFd::from(sock_1), OwnedFd::from(sock_2))) +} + +#[allow(clippy::too_many_arguments)] +pub fn notifications_process( + span: tracing::Span, + cmd: &'static str, + key: Arc>>, + mut env_vars: Vec<(String, String)>, + fd: OwnedFd, + restart_span: tracing::Span, + restart_cmd: &'static str, + restart_key: Arc>>, + restart_env_vars: Vec<(String, String)>, +) -> Process { + env_vars.retain(|v| &v.0 != "WAYLAND_SOCKET"); + + let stdout_span = span.clone(); + let stderr_span = span.clone(); + let env_clone = env_vars.clone(); + Process::new() + .with_executable(cmd) + .with_fds(move || vec![fd]) + .with_on_stdout(move |_, _, line| { + let stdout_span = stdout_span.clone(); + async move { + info!("{}", line); + } + .instrument(stdout_span) + }) + .with_on_stderr(move |_, _, line| { + let stderr_span = stderr_span.clone(); + async move { + warn!("{}", line); + } + .instrument(stderr_span) + }) + .with_on_exit(move |pman, my_key, _, will_restart| { + // force restart of notifications / panel when the other exits + // also update the environment variables to use the new socket + let (my_fd, their_fd) = create_socket().expect("Failed to create notification socket"); + let mut my_env_vars = env_clone.clone(); + if let Some((_k, v)) = my_env_vars + .iter_mut() + .find(|(k, _v)| k == PANEL_NOTIFICATIONS_FD || k == DAEMON_NOTIFICATIONS_FD) + { + *v = my_fd.as_raw_fd().to_string(); + } + + let mut their_env_vars = restart_env_vars.clone(); + if let Some((_k, v)) = their_env_vars + .iter_mut() + .find(|(k, _v)| k == PANEL_NOTIFICATIONS_FD || k == DAEMON_NOTIFICATIONS_FD) + { + *v = their_fd.as_raw_fd().to_string(); + } + + let new_process = notifications_process( + restart_span.clone(), + restart_cmd, + restart_key.clone(), + their_env_vars.clone(), + their_fd, + span.clone(), + cmd, + key.clone(), + my_env_vars.clone(), + ); + let restart_key = restart_key.clone(); + + let mut pman_clone = pman.clone(); + async move { + if will_restart { + if let Err(why) = pman_clone.update_process_env(&my_key, my_env_vars).await { + error!(?why, "Failed to update environment variables"); + } + if let Err(why) = pman_clone + .update_process_fds(&my_key, move || vec![my_fd]) + .await + { + error!(?why, "Failed to update fds"); + } + + let Some(old) = *restart_key.lock().await else { + error!("Couldn't stop previous invocation of {}", cmd); + return; + }; + _ = pman.stop_process(old).await; + + if let Ok(new) = pman.start(new_process).await { + let mut guard = restart_key.lock().await; + *guard = Some(new); + } + } + } + }) + .with_env(env_vars) +} diff --git a/src/process.rs b/src/process.rs new file mode 100644 index 0000000..033b340 --- /dev/null +++ b/src/process.rs @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-3.0-only +use color_eyre::eyre::{Result, WrapErr}; +use rustix::io::FdFlags; +use std::os::unix::prelude::*; + +pub(crate) fn mark_as_not_cloexec(file: &impl AsFd) -> Result<()> { + let flags = rustix::io::fcntl_getfd(file).wrap_err("failed to get GETFD value of stream")?; + rustix::io::fcntl_setfd(file, flags.difference(FdFlags::CLOEXEC)) + .wrap_err("failed to unset CLOEXEC on file") +} diff --git a/src/service.rs b/src/service.rs new file mode 100644 index 0000000..43fc741 --- /dev/null +++ b/src/service.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-3.0-only +use tokio::sync::mpsc; +use zbus::interface; + +pub enum SessionRequest { + Exit, + Restart, +} + +pub struct SessionService { + pub session_tx: mpsc::Sender, +} + +#[interface(name = "com.system76.CosmicSession")] +impl SessionService { + async fn exit(&mut self) { + warn!("exiting session"); + _ = self.session_tx.send(SessionRequest::Exit).await; + } + + async fn restart(&self) { + warn!("restarting session"); + _ = self.session_tx.send(SessionRequest::Restart).await; + } +} diff --git a/src/systemd.rs b/src/systemd.rs new file mode 100644 index 0000000..cbf64d5 --- /dev/null +++ b/src/systemd.rs @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use std::path::Path; +use std::process::{Command, Stdio}; +use std::sync::OnceLock; + +use zbus::Connection; +use zbus::zvariant::{Array, OwnedValue}; + +#[derive(Debug)] +pub struct EnvVar { + pub key: String, + pub value: String, +} + +impl From<(&str, &str)> for EnvVar { + fn from(val: (&str, &str)) -> Self { + EnvVar { + key: val.0.to_owned(), + value: val.1.to_owned(), + } + } +} + +#[cfg(feature = "systemd")] +use zbus_systemd::systemd1::ManagerProxy as SystemdManagerProxy; + +pub async fn set_systemd_environment(key: &str, value: &str) { + run_optional_command( + "systemctl", + &["--user", "set-environment", &format!("{key}={value}")], + ) +} + +pub async fn start_systemd_target() { + run_optional_command( + "systemctl", + &["--user", "start", "--no-block", "cosmic-session.target"], + ) +} + +pub fn stop_systemd_target() { + run_optional_command( + "systemctl", + &["--user", "stop", "--no-block", "cosmic-session.target"], + ) +} + +/// Determine if systemd is used as the init system. This should work on all +/// linux distributions. +pub fn is_systemd_used() -> &'static bool { + static IS_SYSTEMD_USED: OnceLock = OnceLock::new(); + IS_SYSTEMD_USED.get_or_init(|| Path::new("/run/systemd/system").exists()) +} + +#[cfg(feature = "systemd")] +pub async fn get_systemd_env() -> Result, zbus::Error> { + let connection = Connection::session().await?; + let systemd_manager = SystemdManagerProxy::new(&connection).await?; + let systemd_env = systemd_manager.environment().await?; + + let mut out: Vec = Vec::new(); + for i in systemd_env { + if let Some(b) = i.split_once("=") { + out.push(b.into()); + } + } + Ok(out) +} + +#[cfg(feature = "systemd")] +/// Spawn a systemd scope unit with the given name and PIDs. +pub async fn spawn_scope(mut command: String, pids: Vec) -> Result<(), zbus::Error> { + let connection = Connection::session().await?; + let systemd_manager = SystemdManagerProxy::new(&connection).await?; + let pids = OwnedValue::try_from(Array::from(pids)).unwrap(); + let properties: Vec<(String, OwnedValue)> = vec![(String::from("PIDs"), pids)]; + if command.starts_with('/') { + // use the last component of the path as the unit name + command = command.rsplit('/').next().unwrap().to_string(); + } + let scope_name = format!("{}.scope", command); + systemd_manager + .start_transient_unit( + scope_name.to_string(), + String::from("replace"), + properties, + Vec::new(), + ) + .await?; + Ok(()) +} + +/// run a command, but log errors instead of returning them or panicking +fn run_optional_command(cmd: &str, args: &[&str]) { + match Command::new(cmd).args(args).stdin(Stdio::null()).status() { + Ok(status) => { + if !status.success() { + match status.code() { + Some(code) => warn!("{} {}: exit code {}", cmd, args.join(" "), code), + None => warn!("{} {}: terminated by signal", cmd, args.join(" ")), + } + } + } + Err(error) => { + warn!("unable to start {} {}: {}", cmd, args.join(" "), error); + } + } +}