File supergfxctl-4.0.4.obscpio of Package supergfxctl

07070100000000000081A400000000000000000000000161FDA94900000049000000000000000000000000000000000000001D00000000supergfxctl-4.0.4/.gitignore/target
vendor.tar.xz
cargo-config
.idea
vendor-*
vendor_*
.vscode-ctags
07070100000001000081A400000000000000000000000161FDA94900000CCE000000000000000000000000000000000000001F00000000supergfxctl-4.0.4/CHANGELOG.md# Changelog
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [4.0.4] - 2022-02-05
### Changed
- Adjust the kernel cmdline arg code path

## [4.0.3] - 2022-02-04
### Added
- Add config option `no_logind`: Don't use logind to see if all sessions are
  logged out and therefore safe to change mode. This will be useful for people not
  using a login manager, however it is not guaranteed to work unless all graphical
  sessions are ended and nothing is hooking the drivers. Ignored if `always_reboot`
  is set.
- Add config option `logout_timeout_s`: The timeout in seconds to wait for all user
  graphical sessions to end. Default is 3 minutes, 0 = infinite. Ignored if
  `no_logind` or `always_reboot` is set.
- Add new dbus method: `PendingMode`, to check if a mode change is required
- Add new dbus method: `PendingUserAction`, to check if the user is required to perform an action
- Add new dbus method: `Config`, to get the current base config
- Add new dbus method: `SetConfig`, to set the base config
- Add `-p, --pend-action` CLI arg to get the pending user action if any
- Add `-P, --pend-mode` CLI arg to get the pending mode change if any`
- Add ability to read `supergfxd.mode=` from kernel cmdline on startup and set the mode appropriately
### Removed
- CLI option `--force` was unused, it is now removed.

## [4.0.2] - 2022-01-22
### Changed
- Adjust how xorg config is created so that EGPU mode uses it also

## [4.0.1] - 2022-01-20
### Changed
- Fix version upgrade of config
- Recreate the config if parsing fails
- Only write the mode change to config file, don't update live config
### Added
- AMD dedicated + hybrid config for xorg
- "AllowExternalGpus" added to xorg for Nvidia Egpu mode

## [4.0.0] - 2022-01-18
### Added
- Add new dbus method: `Version` to get supergfxd version
- Add new dbus method: `Vendor` to get dGPU vendor name
- Add new dbus method: `Supported` to get list of supported modes
- Add `-v, --version` CLI arg to get supergfxd version
- Add `-V, --vendor` CLI arg to get dGPU vendor name
- Add `-s, --supported` CLI arg to get list of supported modes
- Add new config option: `vfio_save` to reload VFIO on boot
- Add new config option: `compute_save` to reload compute on boot
- Add new config option: `always_reboot` reboot to change modes
### Changed
- Adjust startup to check for ASUS eGPU and dGPU enablement if no modes supported
- If nvidia-drm.modeset=1 is set then save mode and require a reboot by default\
- Add extra check for Nvidia dGPU (fixes Flow 13")
- Properly check the correct device for power status
### Breaking
- Rename Vendor, GetVendor to Mode, GetMode to better reflect their results

## [3.0.0] - 2022-01-10
### Added
- Keep a changelog
### Changed
- Support laptops with AMD dGPU
  + `hybrid`, `integrated`, `vfio` only
  + Modes unsupported by AMD dGPU will return an error
- `nvidia` mode is now `dedicated`
- Don't write the config twice on laptops with hard-mux switch
- CLI print zbus error string if available
- Heavy internal cleanup and refactor to make the project a bit nicer to work with07070100000002000081A400000000000000000000000161FDA949000044CB000000000000000000000000000000000000001D00000000supergfxctl-4.0.4/Cargo.lock# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3

[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
 "memchr",
]

[[package]]
name = "async-io"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b"
dependencies = [
 "concurrent-queue",
 "futures-lite",
 "libc",
 "log",
 "once_cell",
 "parking",
 "polling",
 "slab",
 "socket2",
 "waker-fn",
 "winapi",
]

[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
 "hermit-abi",
 "libc",
 "winapi",
]

[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"

[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"

[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"

[[package]]
name = "cache-padded"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"

[[package]]
name = "cc"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"

[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"

[[package]]
name = "concurrent-queue"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3"
dependencies = [
 "cache-padded",
]

[[package]]
name = "derivative"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "enumflags2"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83c8d82922337cd23a15f88b70d8e4ef5f11da38dd7cdb55e84dd5de99695da0"
dependencies = [
 "enumflags2_derive",
 "serde",
]

[[package]]
name = "enumflags2_derive"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "env_logger"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
dependencies = [
 "atty",
 "humantime",
 "log",
 "regex",
 "termcolor",
]

[[package]]
name = "fastrand"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "779d043b6a0b90cc4c0ed7ee380a6504394cee7efd7db050e3774eee387324b2"
dependencies = [
 "instant",
]

[[package]]
name = "futures"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4"
dependencies = [
 "futures-channel",
 "futures-core",
 "futures-executor",
 "futures-io",
 "futures-sink",
 "futures-task",
 "futures-util",
]

[[package]]
name = "futures-channel"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b"
dependencies = [
 "futures-core",
 "futures-sink",
]

[[package]]
name = "futures-core"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7"

[[package]]
name = "futures-executor"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a"
dependencies = [
 "futures-core",
 "futures-task",
 "futures-util",
]

[[package]]
name = "futures-io"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2"

[[package]]
name = "futures-lite"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48"
dependencies = [
 "fastrand",
 "futures-core",
 "futures-io",
 "memchr",
 "parking",
 "pin-project-lite",
 "waker-fn",
]

[[package]]
name = "futures-macro"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "futures-sink"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508"

[[package]]
name = "futures-task"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72"

[[package]]
name = "futures-util"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164"
dependencies = [
 "futures-channel",
 "futures-core",
 "futures-io",
 "futures-macro",
 "futures-sink",
 "futures-task",
 "memchr",
 "pin-project-lite",
 "pin-utils",
 "slab",
]

[[package]]
name = "gumdrop"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46571f5d540478cf70d2a42dd0d6d8e9f4b9cc7531544b93311e657b86568a0b"
dependencies = [
 "gumdrop_derive",
]

[[package]]
name = "gumdrop_derive"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915ef07c710d84733522461de2a734d4d62a3fd39a4d4f404c2f385ef8618d05"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
 "libc",
]

[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"

[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
 "cfg-if",
]

[[package]]
name = "itoa"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"

[[package]]
name = "libc"
version = "0.2.112"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"

[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
 "cfg-if",
]

[[package]]
name = "logind-zbus"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dca64bea11e365933e0c4a1a9342f0122d1d2822a09c87dab0e1d314adb353a2"
dependencies = [
 "serde",
 "serde_json",
 "zbus",
 "zbus_macros",
 "zvariant",
 "zvariant_derive",
]

[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"

[[package]]
name = "memoffset"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
 "autocfg",
]

[[package]]
name = "nb-connect"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1bb540dc6ef51cfe1916ec038ce7a620daf3a111e2502d745197cd53d6bca15"
dependencies = [
 "libc",
 "socket2",
]

[[package]]
name = "nix"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5e06129fb611568ef4e868c14b326274959aa70ff7776e9d55323531c374945"
dependencies = [
 "bitflags",
 "cc",
 "cfg-if",
 "libc",
 "memoffset",
]

[[package]]
name = "once_cell"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"

[[package]]
name = "parking"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"

[[package]]
name = "pin-project-lite"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c"

[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"

[[package]]
name = "polling"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259"
dependencies = [
 "cfg-if",
 "libc",
 "log",
 "wepoll-ffi",
 "winapi",
]

[[package]]
name = "proc-macro-crate"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785"
dependencies = [
 "toml",
]

[[package]]
name = "proc-macro-crate"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83"
dependencies = [
 "thiserror",
 "toml",
]

[[package]]
name = "proc-macro2"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
dependencies = [
 "unicode-xid",
]

[[package]]
name = "quote"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
dependencies = [
 "proc-macro2",
]

[[package]]
name = "regex"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
 "aho-corasick",
 "memchr",
 "regex-syntax",
]

[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"

[[package]]
name = "ryu"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"

[[package]]
name = "scoped-tls"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"

[[package]]
name = "serde"
version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a"
dependencies = [
 "serde_derive",
]

[[package]]
name = "serde_derive"
version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "serde_json"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c059c05b48c5c0067d4b4b2b4f0732dd65feb52daf7e0ea09cd87e7dadc1af79"
dependencies = [
 "itoa",
 "ryu",
 "serde",
]

[[package]]
name = "serde_repr"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "slab"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"

[[package]]
name = "socket2"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516"
dependencies = [
 "libc",
 "winapi",
]

[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"

[[package]]
name = "supergfxctl"
version = "4.0.3"
dependencies = [
 "env_logger",
 "gumdrop",
 "log",
 "logind-zbus",
 "serde",
 "serde_derive",
 "serde_json",
 "zbus",
 "zvariant",
 "zvariant_derive",
]

[[package]]
name = "syn"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7"
dependencies = [
 "proc-macro2",
 "quote",
 "unicode-xid",
]

[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
 "winapi-util",
]

[[package]]
name = "thiserror"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
dependencies = [
 "thiserror-impl",
]

[[package]]
name = "thiserror-impl"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "toml"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
dependencies = [
 "serde",
]

[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"

[[package]]
name = "waker-fn"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"

[[package]]
name = "wepoll-ffi"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb"
dependencies = [
 "cc",
]

[[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-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
 "winapi",
]

[[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 = "zbus"
version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5983c3d035549ab80db67c844ec83ed271f7c1f2546fd9577c594d34c1b6c85"
dependencies = [
 "async-io",
 "byteorder",
 "derivative",
 "enumflags2",
 "fastrand",
 "futures",
 "nb-connect",
 "nix",
 "once_cell",
 "polling",
 "scoped-tls",
 "serde",
 "serde_repr",
 "zbus_macros",
 "zvariant",
]

[[package]]
name = "zbus_macros"
version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bce54ac7b2150a2fa21ad5842a7470ce2288158d7da1f9bfda8ad455a1c59a97"
dependencies = [
 "proc-macro-crate 0.1.5",
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "zvariant"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a68c7b55f2074489b7e8e07d2d0a6ee6b4f233867a653c664d8020ba53692525"
dependencies = [
 "byteorder",
 "enumflags2",
 "libc",
 "serde",
 "static_assertions",
 "zvariant_derive",
]

[[package]]
name = "zvariant_derive"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4ca5e22593eb4212382d60d26350065bf2a02c34b85bc850474a74b589a3de9"
dependencies = [
 "proc-macro-crate 1.1.0",
 "proc-macro2",
 "quote",
 "syn",
]
07070100000003000081A400000000000000000000000161FDA94900000460000000000000000000000000000000000000001D00000000supergfxctl-4.0.4/Cargo.toml[package]
name = "supergfxctl"
version = "4.0.4"
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
repository = "https://gitlab.com/asus-linux/asusctl"
homepage = "https://gitlab.com/asus-linux/asusctl"
documentation = "https://docs.rs/rog-anime"
description = "Types useful for fancy keyboards on ASUS ROG laptops"
keywords = ["graphics", "nvidia", "switching"]
edition = "2018"
exclude = ["data"]

[features]
daemon = ["env_logger"]
cli = ["gumdrop"]

[lib]
name = "supergfxctl"
path = "src/lib.rs"

[[bin]]
name = "supergfxd"
path = "src/daemon.rs"
required-features = ["daemon"]

[[bin]]
name = "supergfxctl"
path = "src/cli.rs"
required-features = ["cli"]

[dependencies]
serde = "^1.0"
serde_derive = "^1.0"
serde_json = "^1.0"
log = "^0.4"

zbus = "^1.9.1"
zvariant = "^2.8"
zvariant_derive = "^2.8"
logind-zbus = "^0.7.1"

env_logger = { version = "^0.8", optional = true }
gumdrop = { version = "^0.8", optional = true }

[profile.release]
lto = true
debug = false
opt-level = 3
panic = "abort"

[profile.dev]
debug = false
opt-level = 1

[profile.bench]
debug = false
opt-level = 3
07070100000004000081A400000000000000000000000161FDA94900004155000000000000000000000000000000000000001A00000000supergfxctl-4.0.4/LICENSEMozilla Public License Version 2.0
==================================

1. Definitions
--------------

1.1. "Contributor"
    means each individual or legal entity that creates, contributes to
    the creation of, or owns Covered Software.

1.2. "Contributor Version"
    means the combination of the Contributions of others (if any) used
    by a Contributor and that particular Contributor's Contribution.

1.3. "Contribution"
    means Covered Software of a particular Contributor.

1.4. "Covered Software"
    means Source Code Form to which the initial Contributor has attached
    the notice in Exhibit A, the Executable Form of such Source Code
    Form, and Modifications of such Source Code Form, in each case
    including portions thereof.

1.5. "Incompatible With Secondary Licenses"
    means

    (a) that the initial Contributor has attached the notice described
        in Exhibit B to the Covered Software; or

    (b) that the Covered Software was made available under the terms of
        version 1.1 or earlier of the License, but not also under the
        terms of a Secondary License.

1.6. "Executable Form"
    means any form of the work other than Source Code Form.

1.7. "Larger Work"
    means a work that combines Covered Software with other material, in
    a separate file or files, that is not Covered Software.

1.8. "License"
    means this document.

1.9. "Licensable"
    means having the right to grant, to the maximum extent possible,
    whether at the time of the initial grant or subsequently, any and
    all of the rights conveyed by this License.

1.10. "Modifications"
    means any of the following:

    (a) any file in Source Code Form that results from an addition to,
        deletion from, or modification of the contents of Covered
        Software; or

    (b) any new file in Source Code Form that contains any Covered
        Software.

1.11. "Patent Claims" of a Contributor
    means any patent claim(s), including without limitation, method,
    process, and apparatus claims, in any patent Licensable by such
    Contributor that would be infringed, but for the grant of the
    License, by the making, using, selling, offering for sale, having
    made, import, or transfer of either its Contributions or its
    Contributor Version.

1.12. "Secondary License"
    means either the GNU General Public License, Version 2.0, the GNU
    Lesser General Public License, Version 2.1, the GNU Affero General
    Public License, Version 3.0, or any later versions of those
    licenses.

1.13. "Source Code Form"
    means the form of the work preferred for making modifications.

1.14. "You" (or "Your")
    means an individual or a legal entity exercising rights under this
    License. For legal entities, "You" includes any entity that
    controls, is controlled by, or is under common control with You. For
    purposes of this definition, "control" means (a) the power, direct
    or indirect, to cause the direction or management of such entity,
    whether by contract or otherwise, or (b) ownership of more than
    fifty percent (50%) of the outstanding shares or beneficial
    ownership of such entity.

2. License Grants and Conditions
--------------------------------

2.1. Grants

Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:

(a) under intellectual property rights (other than patent or trademark)
    Licensable by such Contributor to use, reproduce, make available,
    modify, display, perform, distribute, and otherwise exploit its
    Contributions, either on an unmodified basis, with Modifications, or
    as part of a Larger Work; and

(b) under Patent Claims of such Contributor to make, use, sell, offer
    for sale, have made, import, and otherwise transfer either its
    Contributions or its Contributor Version.

2.2. Effective Date

The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.

2.3. Limitations on Grant Scope

The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:

(a) for any code that a Contributor has removed from Covered Software;
    or

(b) for infringements caused by: (i) Your and any other third party's
    modifications of Covered Software, or (ii) the combination of its
    Contributions with other software (except as part of its Contributor
    Version); or

(c) under Patent Claims infringed by Covered Software in the absence of
    its Contributions.

This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).

2.4. Subsequent Licenses

No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).

2.5. Representation

Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.

2.6. Fair Use

This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.

2.7. Conditions

Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.

3. Responsibilities
-------------------

3.1. Distribution of Source Form

All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.

3.2. Distribution of Executable Form

If You distribute Covered Software in Executable Form then:

(a) such Covered Software must also be made available in Source Code
    Form, as described in Section 3.1, and You must inform recipients of
    the Executable Form how they can obtain a copy of such Source Code
    Form by reasonable means in a timely manner, at a charge no more
    than the cost of distribution to the recipient; and

(b) You may distribute such Executable Form under the terms of this
    License, or sublicense it under different terms, provided that the
    license for the Executable Form does not attempt to limit or alter
    the recipients' rights in the Source Code Form under this License.

3.3. Distribution of a Larger Work

You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).

3.4. Notices

You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.

3.5. Application of Additional Terms

You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.

4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------

If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.

5. Termination
--------------

5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.

5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.

5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.

************************************************************************
*                                                                      *
*  6. Disclaimer of Warranty                                           *
*  -------------------------                                           *
*                                                                      *
*  Covered Software is provided under this License on an "as is"       *
*  basis, without warranty of any kind, either expressed, implied, or  *
*  statutory, including, without limitation, warranties that the       *
*  Covered Software is free of defects, merchantable, fit for a        *
*  particular purpose or non-infringing. The entire risk as to the     *
*  quality and performance of the Covered Software is with You.        *
*  Should any Covered Software prove defective in any respect, You     *
*  (not any Contributor) assume the cost of any necessary servicing,   *
*  repair, or correction. This disclaimer of warranty constitutes an   *
*  essential part of this License. No use of any Covered Software is   *
*  authorized under this License except under this disclaimer.         *
*                                                                      *
************************************************************************

************************************************************************
*                                                                      *
*  7. Limitation of Liability                                          *
*  --------------------------                                          *
*                                                                      *
*  Under no circumstances and under no legal theory, whether tort      *
*  (including negligence), contract, or otherwise, shall any           *
*  Contributor, or anyone who distributes Covered Software as          *
*  permitted above, be liable to You for any direct, indirect,         *
*  special, incidental, or consequential damages of any character      *
*  including, without limitation, damages for lost profits, loss of    *
*  goodwill, work stoppage, computer failure or malfunction, or any    *
*  and all other commercial damages or losses, even if such party      *
*  shall have been informed of the possibility of such damages. This   *
*  limitation of liability shall not apply to liability for death or   *
*  personal injury resulting from such party's negligence to the       *
*  extent applicable law prohibits such limitation. Some               *
*  jurisdictions do not allow the exclusion or limitation of           *
*  incidental or consequential damages, so this exclusion and          *
*  limitation may not apply to You.                                    *
*                                                                      *
************************************************************************

8. Litigation
-------------

Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.

9. Miscellaneous
----------------

This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.

10. Versions of the License
---------------------------

10.1. New Versions

Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.

10.2. Effect of New Versions

You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.

10.3. Modified Versions

If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).

10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses

If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.

Exhibit A - Source Code Form License Notice
-------------------------------------------

  This Source Code Form is subject to the terms of the Mozilla Public
  License, v. 2.0. If a copy of the MPL was not distributed with this
  file, You can obtain one at http://mozilla.org/MPL/2.0/.

If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.

You may add additional accurate notices of copyright ownership.

Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------

  This Source Code Form is "Incompatible With Secondary Licenses", as
  defined by the Mozilla Public License, v. 2.0.
07070100000005000081A400000000000000000000000161FDA94900000934000000000000000000000000000000000000001B00000000supergfxctl-4.0.4/MakefileVERSION := $(shell grep -Pm1 'version = "(\d.\d.\d)"' Cargo.toml | cut -d'"' -f2)

INSTALL = install
INSTALL_PROGRAM = ${INSTALL} -D -m 0755
INSTALL_DATA = ${INSTALL} -D -m 0644

prefix = /usr
exec_prefix = $(prefix)
bindir = $(exec_prefix)/bin
datarootdir = $(prefix)/share
libdir = $(exec_prefix)/lib

BIN_SD := supergfxd
BIN_SC := supergfxctl
SERVICE := supergfxd.service
PRESET := supergfxd.preset
DBUSCFG := org.supergfxctl.Daemon.conf
X11CFG := 90-nvidia-screen-G05.conf
PMRULES := 90-supergfxd-nvidia-pm.rules

SRC := Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.rs')

DEBUG ?= 0
ifeq ($(DEBUG),0)
	ARGS += --release
	TARGET = release
endif

VENDORED ?= 0
ifeq ($(VENDORED),1)
	ARGS += --frozen
endif

all: build

clean:
	cargo clean

distclean:
	rm -rf .cargo vendor vendor-$(VERSION).tar.xz

install:
	$(INSTALL_PROGRAM) "./target/release/$(BIN_SD)" "$(DESTDIR)$(bindir)/$(BIN_SD)"
	$(INSTALL_PROGRAM) "./target/release/$(BIN_SC)" "$(DESTDIR)$(bindir)/$(BIN_SC)"
	$(INSTALL_DATA) "./data/$(SERVICE)" "$(DESTDIR)$(libdir)/systemd/system/$(SERVICE)"
	$(INSTALL_DATA) "./data/$(PRESET)" "$(DESTDIR)$(libdir)/systemd/system-preset/$(PRESET)"
	$(INSTALL_DATA) "./data/$(DBUSCFG)" "$(DESTDIR)$(datarootdir)/dbus-1/system.d/$(DBUSCFG)"
	$(INSTALL_DATA) "./data/$(X11CFG)" "$(DESTDIR)$(datarootdir)/X11/xorg.conf.d/$(X11CFG)"
	$(INSTALL_DATA) "./data/$(PMRULES)" "$(DESTDIR)$(libdir)/udev/rules.d/$(PMRULES)"

uninstall:
	rm -f "$(DESTDIR)$(bindir)/$(BIN_SC)"
	rm -f "$(DESTDIR)$(bindir)/$(BIN_SD)"
	rm -f "$(DESTDIR)$(libdir)/systemd/system/$(SERVICE)"
	rm -f "$(DESTDIR)$(libdir)/systemd/system-preset/$(PRESET)"
	rm -f "$(DESTDIR)$(datarootdir)/dbus-1/system.d/org.supergfxctl.Daemon.conf"
	rm -f "$(DESTDIR)$(datarootdir)/X11/xorg.conf.d/$(X11CFG)"
	rm -f "$(DESTDIR)$(libdir)/udev/rules.d/$(PMRULES)"

update:
	cargo update

vendor:
	mkdir -p .cargo
	cargo vendor | head -n -1 > .cargo/config
	echo 'directory = "vendor"' >> .cargo/config
	mv .cargo/config ./cargo-config
	rm -rf .cargo
	tar pcfJ vendor-$(VERSION).tar.xz vendor
	rm -rf vendor

build:
ifeq ($(VENDORED),1)
	@echo "version = $(VERSION)"
	tar pxf vendor-$(VERSION).tar.xz
endif
	cargo build --features "daemon cli" $(ARGS)
	strip -s ./target/release/$(BIN_SD)
	strip -s ./target/release/$(BIN_SC)

.PHONY: all clean distclean install uninstall update build
07070100000006000081A400000000000000000000000161FDA949000011FC000000000000000000000000000000000000001C00000000supergfxctl-4.0.4/README.md# supergfxctl

`supergfxd` can switch graphics modes between:
- `hybrid`, enables dGPU-offload mode
- `integrated`, uses the iGPU only and force-disables the dGPU
- `vfio`, binds the dGPU to vfio for VM pass-through

**Nvidia only**

- `dedicated`, uses the dGPU only (note, nvidia + xorg only)
- `compute`, enables Nvidia without Xorg. Useful for ML/Cuda (note, nvidia only)

**ASUS ROG Flow 13" only**

- `egpu`, this is for certain ASUS laptops like 13" Flow to enable external GPU

This switcher conflicts with other gpu switchers like optimus-manager, suse-prime
or ubuntu-prime, system76-power, and bbswitch. If you have issues with `supergfxd`
always defaulting to `integrated` mode on boot then you will need to check for
stray configs blocking nvidia modules from loading in:
- `/etc/modprobe.d/`
- `/usr/lib/modprope.d/`

ASUS laptops require a kernel 5.15.x or newer.

## Building

First you need to install the dev packages required.

* Debian/Ubuntu: `sudo apt update && sudo apt install curl git build-essential`
* Fedora/RHEL: `sudo dnf upgrade && sudo dnf install curl git @development_tools`
* Arch/Manjaro: `sudo pacman -Syu && sudo pacman -S curl git base-devel`

**Install Rust**
```
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs
source ~/.cargo/env
```

**Clone supergfxctl repository**

`git clone https://gitlab.com/asus-linux/supergfxctl.git`

**Install supergfxctl**
```
cd supergfxctl
make && sudo make install
```

**Enable and start the service**

`sudo systemctl enable supergfxd.service --now`

**Add user to group**

Depending on your distro add your username to one of `adm`, `users`, `wheel` then
refresh your session (easiest way is to reboot).

* `sudo usermod -a -G users $USER`

**Switch GPU modes**

* Switching to/from Hybrid and Dedicated modes requires a logout only. (no reboot)
* Switching between integrated/compute/vfio is instant. (no logout or reboot)
* Mode can be set via kernel cmdline with `supergfxd.mode=`. Capitalisation does not matter.

| GPU Modes  | Command                       |
|------------|-------------------------------|
| Integrated | supergfxctl --mode integrated |
| Dedicated  | supergfxctl --mode dedicated  |
| Hybrid     | supergfxctl --mode hybrid     |
| Compute    | supergfxctl --mode compute    |
| VFIO       | supergfxctl --mode vfio       |

#### Required actions in distro

**NVIDIA Rebootless note:** You must edit `/etc/default/grub` to edit `nvidia-drm.modeset=1`
to `nvidia-drm.modeset=0` on the line `GRUB_CMDLINE_LINUX=` and then recreate your grub config.

In fedora you can do this with `sudo grub2-mkconfig -o /etc/grub2.cfg` - other distro may be
similar but with a different config location. It's possible that graphics driver updates
may change this.

If `nvidia-drm.modeset=1` is used then supergfxd requires a reboot to change modes.

#### supergfxctl

```
supergfxctl --help
Optional arguments:
  -h, --help         print help message
  -m, --mode         Set graphics mode
  -v, --version      Get supergfxd version
  -g, --get          Get the current mode
  -s, --supported    Get the supported modes
  -V, --vendor       Get the dGPU vendor name
  -S, --status       Get the current power status
  -p, --pend-action  Get the pending user action if any
  -P, --pend-mode    Get the pending mode change if any
```

#### Config options

1. `mode`: <MODE> : any of supported modes, must be capitalised
2. `vfio_enable` <bool> : enable vfio switching for dGPU passthrough
3. `vfio_save` <bool> : save vfio state in mode (so it sticks between boots)
4. `compute_save` <bool> : save compute state in mode (so it sticks between boots)
5. `always_reboot` <bool> : always require a reboot to change modes (helps some laptops)
6. `no_logind` <bool> : don't use logind to see if all sessions are logged out and therefore safe to change mode. This will be useful for people not using a login manager. Ignored if `always_reboot` is set.
7. `logout_timeout_s` <u64> : the timeout in seconds to wait for all user graphical sessions to end. Default is 3 minutes, 0 = infinite. Ignored if `no_logind` or `always_reboot` is set.

#### Graphics switching notes

**G-Sync note:** Some laptops are capable of using the dGPU as the sole GPU in the system which is generally to enable g-sync on the laptop display panel. This is controlled by asusctl at this time, and may be added to supergfxd later.

**vfio note:** The vfio modules *must not* be compiled into the kernel, they need
to be separate modules. If you don't plan to use vfio mode then you can ignore this
otherwise you may need a custom built kernel.
07070100000007000041ED00000000000000000000000161FDA94900000000000000000000000000000000000000000000001700000000supergfxctl-4.0.4/data07070100000008000081A400000000000000000000000161FDA94900000059000000000000000000000000000000000000003100000000supergfxctl-4.0.4/data/90-nvidia-screen-G05.confSection "ServerLayout"
  Identifier "layout"
  Option "AllowNVIDIAGPUScreens"
EndSection
07070100000009000081A400000000000000000000000161FDA949000002A8000000000000000000000000000000000000003400000000supergfxctl-4.0.4/data/90-supergfxd-nvidia-pm.rules# Enable runtime PM for NVIDIA VGA/3D controller devices on driver bind
ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="auto"
ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="auto"

# Disable runtime PM for NVIDIA VGA/3D controller devices on driver unbind
ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="on"
ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="on"
0707010000000A000081A400000000000000000000000161FDA949000003F7000000000000000000000000000000000000003300000000supergfxctl-4.0.4/data/org.supergfxctl.Daemon.conf<!DOCTYPE busconfig PUBLIC
          "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
          "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
    <policy group="adm">
        <allow send_destination="org.supergfxctl.Daemon"/>
        <allow receive_sender="org.supergfxctl.Daemon"/>
    </policy>
    <policy group="sudo">
        <allow send_destination="org.supergfxctl.Daemon"/>
        <allow receive_sender="org.supergfxctl.Daemon"/>
    </policy>
    <policy group="users">
        <allow send_destination="org.supergfxctl.Daemon"/>
        <allow receive_sender="org.supergfxctl.Daemon"/>
    </policy>
    <policy group="wheel">
        <allow send_destination="org.supergfxctl.Daemon"/>
        <allow receive_sender="org.supergfxctl.Daemon"/>
    </policy>
    <policy user="root">
        <allow own="org.supergfxctl.Daemon"/>
        <allow send_destination="org.supergfxctl.Daemon"/>
        <allow receive_sender="org.supergfxctl.Daemon"/>
    </policy>
</busconfig>
0707010000000B000081A400000000000000000000000161FDA94900000019000000000000000000000000000000000000002800000000supergfxctl-4.0.4/data/supergfxd.presetenable supergfxd.service
0707010000000C000081A400000000000000000000000161FDA9490000018C000000000000000000000000000000000000002900000000supergfxctl-4.0.4/data/supergfxd.service[Unit]
Description=SUPERGFX
StartLimitInterval=200
StartLimitBurst=2
Before=multi-user.target

[Service]
Environment=IS_SUPERGFX_SERVICE=1
ExecStart=/usr/bin/supergfxd
Restart=on-failure
Restart=always
RestartSec=1
Type=dbus
BusName=org.supergfxctl.Daemon
SELinuxContext=system_u:system_r:unconfined_t:s0
#SELinuxContext=system_u:object_r:modules_object_t:s0

[Install]
WantedBy=multi-user.target0707010000000D000041ED00000000000000000000000161FDA94900000000000000000000000000000000000000000000001600000000supergfxctl-4.0.4/src0707010000000E000081A400000000000000000000000161FDA94900001832000000000000000000000000000000000000001D00000000supergfxctl-4.0.4/src/cli.rs//! Basic CLI tool to control the `supergfxd` daemon

use std::{env::args, process::Command, sync::mpsc::channel};
use supergfxctl::{
    error::GfxError,
    gfx_vendors::{GfxMode, GfxRequiredUserAction},
    nvidia_drm_modeset,
    special_asus::{get_asus_gsync_gfx_mode, has_asus_gsync_gfx_mode},
    zbus_proxy::GfxProxy,
};

use gumdrop::Options;
use zbus::Connection;

#[derive(Default, Clone, Copy, Options)]
struct CliStart {
    #[options(help = "print help message")]
    help: bool,
    #[options(meta = "", help = "Set graphics mode")]
    mode: Option<GfxMode>,
    #[options(help = "Get supergfxd version")]
    version: bool,
    #[options(help = "Get the current mode")]
    get: bool,
    #[options(help = "Get the supported modes")]
    supported: bool,
    #[options(help = "Get the dGPU vendor name")]
    vendor: bool,
    #[options(help = "Get the current power status")]
    status: bool,
    #[options(help = "Get the pending user action if any")]
    pend_action: bool,
    #[options(help = "Get the pending mode change if any")]
    pend_mode: bool,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let args: Vec<String> = args().skip(1).collect();

    match CliStart::parse_args_default(&args) {
        Ok(command) => {
            do_gfx(command).map_err(|err|{
                eprintln!("Graphics mode change error.");
                if !check_systemd_unit_enabled("supergfxd") {
                    eprintln!("\x1b[0;31msupergfxd is not enabled, enable it with `systemctl enable supergfxd\x1b[0m");
                } else if !check_systemd_unit_active("supergfxd") {
                    eprintln!("\x1b[0;31msupergfxd is not running, start it with `systemctl start supergfxd\x1b[0m");
                } else {
                    eprintln!("Please check `journalctl -b -u supergfxd`, and `systemctl status supergfxd`");
                    match &err {
                        GfxError::Zbus(err) => {
                            match err {
                                zbus::Error::MethodError(_,s,_) => {
                                    if let Some(text) = s {
                                        eprintln!("\x1b[0;31m{}\x1b[0m", text);
                                        std::process::exit(1);
                                    }
                                }
                                _ => {},
                            }
                        }
                        _ => {},
                    }
                }
                eprintln!("Error: {}", err);
                std::process::exit(1);
            }).ok();
        }
        Err(err) => {
            eprintln!("Error: {}", err);
            std::process::exit(1);
        }
    }

    Ok(())
}

fn do_gfx(command: CliStart) -> Result<(), GfxError> {
    if command.mode.is_none()
        && !command.get
        && !command.version
        && !command.supported
        && !command.vendor
        && !command.status
        && !command.pend_action
        && !command.pend_mode
        || command.help
    {
        println!("{}", command.self_usage());
    }

    let conn = Connection::new_system()?;
    let proxy = GfxProxy::new(&conn)?;

    let (tx, rx) = channel();
    proxy.connect_notify_action(tx)?;

    if let Some(mode) = command.mode {
        if has_asus_gsync_gfx_mode() && get_asus_gsync_gfx_mode()? == 1 {
            eprintln!("You can not change modes until you turn dedicated/G-Sync off and reboot");
            std::process::exit(1);
        }

        proxy.write_mode(&mode)?;

        loop {
            proxy.next_signal()?;

            if let Ok(res) = rx.try_recv() {
                match res {
                    GfxRequiredUserAction::Integrated => {
                        eprintln!(
                            "You must change to Integrated before you can change to {}",
                            <&str>::from(mode)
                        );
                        std::process::exit(1);
                    }
                    GfxRequiredUserAction::Logout | GfxRequiredUserAction::Reboot => {
                        if nvidia_drm_modeset()? {
                            println!("\x1b[0;31mRebootless mode requires nvidia-drm.modeset=0 to be set on the kernel cmdline\n\x1b[0m");
                        }
                        println!(
                            "Graphics mode changed to {}. Required user action is: {}",
                            <&str>::from(mode),
                            <&str>::from(&res)
                        );
                    }
                    GfxRequiredUserAction::None => {
                        println!("Graphics mode changed to {}", <&str>::from(mode));
                    }
                }
            }
            std::process::exit(0)
        }
    }
    if command.version {
        let res = proxy.get_version()?;
        println!("{}", res);
    }
    if command.get {
        let res = proxy.get_mode()?;
        println!("{}", <&str>::from(res));
    }
    if command.supported {
        let res = proxy.get_supported_modes()?;
        println!("{:?}", res);
    }
    if command.vendor {
        let res = proxy.get_vendor()?;
        println!("{}", res);
    }
    if command.status {
        let res = proxy.get_pwr()?;
        println!("{}", <&str>::from(&res));
    }
    if command.pend_action {
        let res = proxy.pending_user_action()?;
        println!("{}", <&str>::from(&res));
    }
    if command.pend_mode {
        let res = proxy.pending_mode()?;
        println!("{}", <&str>::from(&res));
    }

    Ok(())
}

fn check_systemd_unit_active(name: &str) -> bool {
    if let Ok(out) = Command::new("systemctl")
        .arg("is-active")
        .arg(name)
        .output()
    {
        let buf = String::from_utf8_lossy(&out.stdout);
        return !buf.contains("inactive") && !buf.contains("failed");
    }
    false
}

fn check_systemd_unit_enabled(name: &str) -> bool {
    if let Ok(out) = Command::new("systemctl")
        .arg("is-enabled")
        .arg(name)
        .output()
    {
        let buf = String::from_utf8_lossy(&out.stdout);
        return buf.contains("enabled");
    }
    false
}
0707010000000F000081A400000000000000000000000161FDA949000023E0000000000000000000000000000000000000002000000000supergfxctl-4.0.4/src/config.rsuse log::{error, info, warn};
use serde_derive::{Deserialize, Serialize};
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use zvariant_derive::Type;

use crate::config_old::GfxConfig300;
use crate::error::GfxError;
use crate::gfx_devices::DiscreetGpu;
use crate::gfx_vendors::{GfxMode, GfxRequiredUserAction};
use crate::{
    MODPROBE_INTEGRATED, MODPROBE_NVIDIA_BASE, MODPROBE_NVIDIA_DRM_MODESET, MODPROBE_PATH,
    MODPROBE_VFIO, PRIMARY_GPU_AMD_BEGIN, PRIMARY_GPU_END, PRIMARY_GPU_NVIDIA_BEGIN,
    PRIMARY_IS_DGPU, PRIMARY_IS_EGPU, XORG_FILE, XORG_PATH,
};

/// Cleaned config for passing over dbus only
#[derive(Debug, Clone, Deserialize, Serialize, Type)]
pub struct GfxConfigDbus {
    pub mode: GfxMode,
    pub vfio_enable: bool,
    pub vfio_save: bool,
    pub compute_save: bool,
    pub always_reboot: bool,
    pub no_logind: bool,
    pub logout_timeout_s: u64,
}

impl From<&GfxConfig> for GfxConfigDbus {
    fn from(c: &GfxConfig) -> Self {
        Self {
            mode: c.mode,
            vfio_enable: c.vfio_enable,
            vfio_save: c.vfio_save,
            compute_save: c.compute_save,
            always_reboot: c.always_reboot,
            no_logind: c.no_logind,
            logout_timeout_s: c.logout_timeout_s,
        }
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct GfxConfig {
    #[serde(skip)]
    pub config_path: String,
    /// The current mode set, also applies on boot
    pub mode: GfxMode,
    /// Only for temporary modes like compute or vfio
    #[serde(skip)]
    pub tmp_mode: Option<GfxMode>,
    /// Just for tracking the requested mode change in rebootless mode
    #[serde(skip)]
    pub pending_mode: Option<GfxMode>,
    /// Just for tracking the required user action
    #[serde(skip)]
    pub pending_action: Option<GfxRequiredUserAction>,
    /// Set if vfio option is enabled. This requires the vfio drivers to be built as modules
    pub vfio_enable: bool,
    /// Save the VFIO mode so that it is reloaded on boot
    pub vfio_save: bool,
    /// Save the Compute mode so that it is reloaded on boot
    pub compute_save: bool,
    /// Should always reboot?
    pub always_reboot: bool,
    /// Don't use logind to see if all sessions are logged out and therefore safe to change mode
    pub no_logind: bool,
    /// The timeout in seconds to wait for all user graphical sessions to end. Default is 3 minutes, 0 = infinite. Ignored if `no_logind` or `always_reboot` is set.
    pub logout_timeout_s: u64,
}

impl GfxConfig {
    fn new(config_path: String) -> Self {
        Self {
            config_path,
            mode: GfxMode::Hybrid,
            tmp_mode: None,
            pending_mode: None,
            pending_action: None,
            vfio_enable: false,
            vfio_save: false,
            compute_save: false,
            always_reboot: false,
            no_logind: false,
            logout_timeout_s: 180,
        }
    }

    /// `load` will attempt to read the config, and panic if the dir is missing
    pub fn load(config_path: String) -> Self {
        let mut file = OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .open(&config_path)
            .unwrap_or_else(|_| panic!("The directory {} is missing", config_path)); // okay to cause panic here
        let mut buf = String::new();
        let mut config;
        if let Ok(read_len) = file.read_to_string(&mut buf) {
            if read_len == 0 {
                config = Self::new(config_path);
            } else if let Ok(data) = serde_json::from_str(&buf) {
                config = data;
                config.config_path = config_path;
            } else if let Ok(data) = serde_json::from_str(&buf) {
                let old: GfxConfig300 = data;
                config = old.into();
                config.config_path = config_path;
            } else {
                warn!("Could not deserialise {}, recreating", config_path);
                config = GfxConfig::new(config_path);
            }
        } else {
            config = Self::new(config_path)
        }
        config.write();
        config
    }

    pub fn read(&mut self) {
        let mut file = OpenOptions::new()
            .read(true)
            .open(&self.config_path)
            .unwrap_or_else(|err| panic!("Error reading {}: {}", self.config_path, err));
        let mut buf = String::new();
        if let Ok(l) = file.read_to_string(&mut buf) {
            if l == 0 {
                warn!("File is empty {}", self.config_path);
            } else {
                let mut x: Self = serde_json::from_str(&buf)
                    .unwrap_or_else(|_| panic!("Could not deserialise {}", self.config_path));
                // copy over serde skipped values
                x.tmp_mode = self.tmp_mode;
                *self = x;
            }
        }
    }

    pub fn write(&self) {
        let mut file = File::create(&self.config_path).expect("Couldn't overwrite config");
        let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed");
        file.write_all(json.as_bytes())
            .unwrap_or_else(|err| error!("Could not write config: {}", err));
    }
}

/// Creates the full modprobe.conf required for vfio pass-through
fn create_vfio_conf(devices: &DiscreetGpu) -> Vec<u8> {
    let mut vifo = MODPROBE_VFIO.to_vec();
    for (f_count, func) in devices.functions().iter().enumerate() {
        let vendor = func.vendor().unwrap();
        let device = func.device().unwrap();
        unsafe {
            vifo.append(format!("{:x}", vendor).as_mut_vec());
        }
        vifo.append(&mut vec![b':']);
        unsafe {
            vifo.append(format!("{:x}", device).as_mut_vec());
        }
        if f_count < devices.functions().len() - 1 {
            vifo.append(&mut vec![b',']);
        }
    }
    vifo.append(&mut vec![b',']);

    let mut conf = MODPROBE_INTEGRATED.to_vec();
    conf.append(&mut vifo);
    conf
}

pub(crate) fn create_modprobe_conf(vendor: GfxMode, devices: &DiscreetGpu) -> Result<(), GfxError> {
    info!("Writing {}", MODPROBE_PATH);
    let content = match vendor {
        GfxMode::Dedicated | GfxMode::Hybrid | GfxMode::Egpu => {
            if devices.is_nvidia() {
                let mut base = MODPROBE_NVIDIA_BASE.to_vec();
                base.append(&mut MODPROBE_NVIDIA_DRM_MODESET.to_vec());
                base
            } else if devices.is_amd() {
                return Ok(());
            } else {
                warn!("No valid modprobe config for device");
                return Ok(());
            }
        }
        GfxMode::Vfio => create_vfio_conf(devices),
        GfxMode::Integrated => MODPROBE_INTEGRATED.to_vec(),
        GfxMode::Compute => MODPROBE_NVIDIA_BASE.to_vec(),
        GfxMode::None => vec![],
    };

    let mut file = std::fs::OpenOptions::new()
        .create(true)
        .truncate(true)
        .write(true)
        .open(MODPROBE_PATH)
        .map_err(|err| GfxError::Path(MODPROBE_PATH.into(), err))?;

    file.write_all(&content)
        .and_then(|_| file.sync_all())
        .map_err(|err| GfxError::Write(MODPROBE_PATH.into(), err))?;

    Ok(())
}

/// Write the appropriate xorg config for the chosen mode
pub(crate) fn create_xorg_conf(mode: GfxMode, gfx: &DiscreetGpu) -> Result<(), GfxError> {
    // Don't want these modes hooked by xorg
    if matches!(mode, GfxMode::Compute | GfxMode::Vfio) {
        return Ok(());
    }
    let text = if gfx.is_nvidia() {
        if mode == GfxMode::Dedicated {
            [
                PRIMARY_GPU_NVIDIA_BEGIN,
                PRIMARY_IS_DGPU,
                if matches!(mode, GfxMode::Egpu) {
                    PRIMARY_IS_EGPU
                } else {
                    &[]
                },
                PRIMARY_GPU_END,
            ]
            .concat()
        } else {
            [
                PRIMARY_GPU_NVIDIA_BEGIN,
                if matches!(mode, GfxMode::Egpu) {
                    PRIMARY_IS_EGPU
                } else {
                    &[]
                },
                PRIMARY_GPU_END,
            ]
            .concat()
        }
    } else if gfx.is_amd() {
        if mode == GfxMode::Dedicated {
            [PRIMARY_GPU_AMD_BEGIN, PRIMARY_IS_DGPU, PRIMARY_GPU_END].concat()
        } else {
            [PRIMARY_GPU_AMD_BEGIN, PRIMARY_GPU_END].concat()
        }
    } else {
        warn!("No valid xorg config for device");
        return Ok(());
    };

    if !Path::new(XORG_PATH).exists() {
        std::fs::create_dir(XORG_PATH).map_err(|err| GfxError::Write(XORG_PATH.into(), err))?;
    }

    let mut path = PathBuf::from(XORG_PATH);
    path.push(XORG_FILE);
    info!("Writing {}", path.display());
    let mut file = std::fs::OpenOptions::new()
        .create(true)
        .truncate(true)
        .write(true)
        .open(&path)
        .map_err(|err| GfxError::Write(format!("{}", path.display()), err))?;

    file.write_all(&text)
        .and_then(|_| file.sync_all())
        .map_err(|err| GfxError::Write(MODPROBE_PATH.into(), err))?;
    Ok(())
}
07070100000010000081A400000000000000000000000161FDA94900000B21000000000000000000000000000000000000002400000000supergfxctl-4.0.4/src/config_old.rsuse serde_derive::{Deserialize, Serialize};

use crate::{config::GfxConfig, gfx_vendors::GfxMode};

#[derive(Debug, PartialEq, Copy, Clone, Deserialize, Serialize)]
pub enum GfxMode300 {
    Hybrid,
    Nvidia,
    Integrated,
    Compute,
    Vfio,
    Egpu,
}

impl From<GfxMode300> for GfxMode {
    fn from(m: GfxMode300) -> Self {
        match m {
            GfxMode300::Hybrid => GfxMode::Hybrid,
            GfxMode300::Nvidia => GfxMode::Dedicated,
            GfxMode300::Integrated => GfxMode::Integrated,
            GfxMode300::Compute => GfxMode::Compute,
            GfxMode300::Vfio => GfxMode::Vfio,
            GfxMode300::Egpu => GfxMode::Egpu,
        }
    }
}

#[derive(Deserialize, Serialize)]
pub struct GfxConfig300 {
    #[serde(skip)]
    config_path: String,
    /// The current mode set, also applies on boot
    pub gfx_mode: GfxMode,
    /// Only for informational purposes
    #[serde(skip)]
    pub gfx_tmp_mode: Option<GfxMode>,
    /// Set if graphics management is enabled
    pub gfx_managed: bool,
    /// Set if vfio option is enabled. This requires the vfio drivers to be built as modules
    pub gfx_vfio_enable: bool,
}

impl From<GfxConfig300> for GfxConfig {
    fn from(old: GfxConfig300) -> Self {
        GfxConfig {
            config_path: old.config_path,
            mode: old.gfx_mode.into(),
            tmp_mode: old.gfx_tmp_mode,
            pending_mode: None,
            pending_action: None,
            vfio_enable: old.gfx_vfio_enable,
            vfio_save: false,
            compute_save: false,
            always_reboot: false,
            no_logind: false,
            logout_timeout_s: 180,
        }
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct GfxConfig402 {
    #[serde(skip)]
    pub config_path: String,
    /// The current mode set, also applies on boot
    pub mode: GfxMode,
    /// Only for informational purposes
    #[serde(skip)]
    pub tmp_mode: Option<GfxMode>,
    /// Set if vfio option is enabled. This requires the vfio drivers to be built as modules
    pub vfio_enable: bool,
    /// Save the VFIO mode so that it is reloaded on boot
    pub vfio_save: bool,
    /// Save the Compute mode so that it is reloaded on boot
    pub compute_save: bool,
    /// Should always reboot?
    pub always_reboot: bool,
}

impl From<GfxConfig402> for GfxConfig {
    fn from(old: GfxConfig402) -> Self {
        GfxConfig {
            config_path: old.config_path,
            mode: old.mode,
            tmp_mode: old.tmp_mode,
            pending_mode: None,
            pending_action: None,
            vfio_enable: old.vfio_enable,
            vfio_save: old.vfio_save,
            compute_save: old.compute_save,
            always_reboot: old.always_reboot,
            no_logind: false,
            logout_timeout_s: 180,
        }
    }
}
07070100000011000081A400000000000000000000000161FDA94900004FD3000000000000000000000000000000000000002400000000supergfxctl-4.0.4/src/controller.rsuse ::zbus::Connection;
use log::{error, info, warn};
use logind_zbus::{
    types::{SessionClass, SessionInfo, SessionState, SessionType},
    ManagerProxy, SessionProxy,
};
use std::time::Instant;
use std::{process::Command, thread::sleep, time::Duration};
use std::{
    sync::atomic::{AtomicBool, Ordering},
    sync::Arc,
    sync::Mutex,
};

use crate::{
    config::{create_modprobe_conf, create_xorg_conf},
    error::GfxError,
    gfx_devices::DiscreetGpu,
    gfx_vendors::GfxVendor,
    pci_device::{rescan_pci_bus, RuntimePowerManagement},
    special_asus::{
        asus_egpu_exists, asus_egpu_set_status, get_asus_gsync_gfx_mode, has_asus_gsync_gfx_mode,
        is_gpu_enabled,
    },
    *,
};

use super::config::GfxConfig;
use super::gfx_vendors::{GfxMode, GfxRequiredUserAction};

pub struct CtrlGraphics {
    pub(crate) dgpu: DiscreetGpu,
    pub(crate) config: Arc<Mutex<GfxConfig>>,
    thread_exit: Arc<AtomicBool>,
}

impl CtrlGraphics {
    pub fn new(config: Arc<Mutex<GfxConfig>>) -> Result<CtrlGraphics, GfxError> {
        Ok(CtrlGraphics {
            dgpu: DiscreetGpu::new()?,
            config,
            thread_exit: Arc::new(AtomicBool::new(false)),
        })
    }

    pub fn dgpu(&self) -> &DiscreetGpu {
        &self.dgpu
    }

    /// Force re-init of all state, including reset of device state
    pub fn reload(&mut self) -> Result<(), GfxError> {
        let vfio_enable;
        let vfio_save;
        let compute_save;

        if let Ok(config) = self.config.lock() {
            vfio_enable = config.vfio_enable;
            vfio_save = config.vfio_save;
            compute_save = config.compute_save;
        } else {
            error!("Could not lock config file on reload action");
            return Ok(());
        }

        let mode = get_kernel_cmdline_mode()?
            .map(|mode| {
                warn!("Graphic mode {:?} set on kernel cmdline", mode);
                let mut save = false;
                match mode {
                    GfxMode::Dedicated | GfxMode::Hybrid | GfxMode::Integrated => save = true,
                    GfxMode::Compute => {
                        if compute_save {
                            save = true;
                        }
                    }
                    GfxMode::Vfio => {
                        if vfio_save && vfio_enable {
                            save = true;
                        }
                    }
                    GfxMode::Egpu => {
                        if asus_egpu_exists() {
                            save = true;
                        }
                    }
                    GfxMode::None => {}
                }

                if save {
                    if let Ok(mut config) = self.config.lock() {
                        config.mode = mode;
                        config.write();
                    }
                }

                mode
            })
            .unwrap_or(self.get_gfx_mode()?);

        if matches!(mode, GfxMode::Vfio) && !vfio_enable {
            warn!("Tried to set vfio mode but it is not enabled");
            return Ok(());
        }

        if matches!(mode, GfxMode::Egpu) && !asus_egpu_exists() {
            warn!("Tried to set egpu mode but it is not supported");
            return Ok(());
        }

        Self::do_mode_setup_tasks(mode, vfio_enable, &self.dgpu)?;

        info!("Reloaded gfx mode: {:?}", mode);
        Ok(())
    }

    /// Save the selected `Mode` to config
    fn save_gfx_mode(mode: GfxMode, config: Arc<Mutex<GfxConfig>>) {
        if let Ok(mut config) = config.lock() {
            config.mode = mode;
            config.write();
        }
    }

    /// Associated method to get which mode is set
    pub(crate) fn get_gfx_mode(&self) -> Result<GfxMode, GfxError> {
        if let Ok(config) = self.config.lock() {
            if let Some(mode) = config.tmp_mode {
                return Ok(mode);
            }
            return Ok(config.mode);
        }
        Err(GfxError::ParseVendor)
    }

    ///
    pub(crate) fn get_pending_mode(&self) -> GfxMode {
        if let Ok(config) = self.config.lock() {
            if let Some(mode) = config.pending_mode {
                return mode;
            }
        }
        GfxMode::None
    }

    ///
    pub(crate) fn get_pending_user_action(&self) -> GfxRequiredUserAction {
        if let Ok(config) = self.config.lock() {
            if let Some(action) = config.pending_action {
                return action;
            }
        }
        GfxRequiredUserAction::None
    }

    /// Associated method to get list of supported modes
    pub(crate) fn get_supported_modes(&self) -> Vec<GfxMode> {
        if matches!(self.dgpu.vendor(), GfxVendor::Unknown) {
            return vec![GfxMode::Integrated];
        }

        let mut list = vec![GfxMode::Integrated, GfxMode::Hybrid, GfxMode::Dedicated];

        if self.dgpu.is_nvidia() {
            list.push(GfxMode::Compute);
        }

        if let Ok(config) = self.config.lock() {
            if config.vfio_enable {
                list.push(GfxMode::Vfio);
            }
        }

        if asus_egpu_exists() {
            list.push(GfxMode::Egpu);
        }

        list
    }

    /// Associated method to get which vendor the dgpu is from
    pub(crate) fn get_gfx_vendor(&self) -> GfxVendor {
        self.dgpu.vendor()
    }

    fn do_display_manager_action(action: &str) -> Result<(), GfxError> {
        let mut cmd = Command::new("systemctl");
        cmd.arg(action);
        cmd.arg(DISPLAY_MANAGER);

        let status = cmd
            .status()
            .map_err(|err| GfxError::Command(format!("{:?}", cmd), err))?;
        if !status.success() {
            let msg = format!(
                "systemctl {} {} failed: {:?}",
                action, DISPLAY_MANAGER, status
            );
            return Err(GfxError::DisplayManagerAction(msg, status));
        }
        Ok(())
    }

    fn wait_display_manager_state(state: &str) -> Result<(), GfxError> {
        let mut cmd = Command::new("systemctl");
        cmd.arg("is-active");
        cmd.arg(DISPLAY_MANAGER);

        let mut count = 0;

        while count <= (4 * 3) {
            // 3 seconds max
            let output = cmd
                .output()
                .map_err(|err| GfxError::Command(format!("{:?}", cmd), err))?;
            if output.stdout.starts_with(state.as_bytes()) {
                return Ok(());
            }
            std::thread::sleep(std::time::Duration::from_millis(250));
            count += 1;
        }
        Err(GfxError::DisplayManagerTimeout(state.into()))
    }

    /// Determine if we need to logout/thread. Integrated<->Vfio mode does not
    /// require logout.
    fn mode_change_action(&self, vendor: GfxMode) -> GfxRequiredUserAction {
        if nvidia_drm_modeset()
            .map_err(|e| {
                error!("mode_change_action error: {}", e);
                e
            })
            .unwrap_or(false)
        {
            return GfxRequiredUserAction::Reboot;
        }

        if let Ok(config) = self.config.lock() {
            if config.always_reboot {
                return GfxRequiredUserAction::Reboot;
            }

            let current = config.mode;
            // Modes that can switch without logout
            if matches!(
                current,
                GfxMode::Integrated | GfxMode::Vfio | GfxMode::Compute
            ) && matches!(
                vendor,
                GfxMode::Integrated | GfxMode::Vfio | GfxMode::Compute
            ) {
                return GfxRequiredUserAction::None;
            }
            // Modes that require a switch to integrated first
            if matches!(current, GfxMode::Dedicated | GfxMode::Hybrid)
                && matches!(vendor, GfxMode::Compute | GfxMode::Vfio)
            {
                return GfxRequiredUserAction::Integrated;
            }
        }
        GfxRequiredUserAction::Logout
    }

    /// Do a full setup flow for the chosen mode:
    ///
    /// Tasks:
    /// - rescan for devices
    /// - write xorg config
    /// - write modprobe config
    ///   + add drivers
    ///   + or remove drivers and devices
    ///
    /// The daemon needs direct access to this function when it detects that the
    /// bios has G-Sync switch is enabled
    pub fn do_mode_setup_tasks(
        mode: GfxMode,
        vfio_enable: bool,
        devices: &DiscreetGpu,
    ) -> Result<(), GfxError> {
        // Rescan before doing remove or add drivers
        rescan_pci_bus()?;
        devices.set_runtime_pm(RuntimePowerManagement::Auto)?;

        create_xorg_conf(mode, devices)?;
        create_modprobe_conf(mode, devices)?;

        match mode {
            GfxMode::Dedicated | GfxMode::Hybrid | GfxMode::Compute => {
                if vfio_enable {
                    for driver in VFIO_DRIVERS.iter() {
                        do_driver_action(driver, "rmmod")?;
                    }
                }
                if asus_egpu_exists() {
                    asus_egpu_set_status(false)?;
                }
                devices.do_driver_action("modprobe")?;
            }
            GfxMode::Vfio => {
                if vfio_enable {
                    do_driver_action("nouveau", "rmmod")?;
                    devices.do_driver_action("rmmod")?;
                    devices.unbind()?;
                    do_driver_action("vfio-pci", "modprobe")?;
                } else {
                    return Err(GfxError::VfioDisabled);
                }
            }
            GfxMode::Integrated => {
                do_driver_action("nouveau", "rmmod")?;
                if vfio_enable {
                    for driver in VFIO_DRIVERS.iter() {
                        do_driver_action(driver, "rmmod")?;
                    }
                }
                devices.do_driver_action("rmmod")?;
                devices.unbind_remove()?;
                // This can only be done *after* the drivers are removed or a
                // hardlock will be caused
                if asus_egpu_exists() {
                    asus_egpu_set_status(false)?;
                }
            }
            GfxMode::Egpu => {
                if !asus_egpu_exists() {
                    warn!("eGPU mode attempted while unsupported by this machine");
                    return Err(GfxError::NotSupported(
                        "eGPU mode not supported".to_string(),
                    ));
                }

                if vfio_enable {
                    for driver in VFIO_DRIVERS.iter() {
                        do_driver_action(driver, "rmmod")?;
                    }
                }

                asus_egpu_set_status(true)?;

                devices.do_driver_action("modprobe")?;
            }
            GfxMode::None => {}
        }
        Ok(())
    }

    /// Check if the user has any graphical uiser sessions that are active or online
    fn graphical_user_sessions_exist(
        connection: &Connection,
        sessions: &[SessionInfo],
    ) -> Result<bool, GfxError> {
        for session in sessions {
            let session_proxy = SessionProxy::new(connection, session)?;
            if session_proxy.get_class()? == SessionClass::User {
                match session_proxy.get_type()? {
                    SessionType::X11 | SessionType::Wayland | SessionType::MIR => {
                        match session_proxy.get_state()? {
                            SessionState::Online | SessionState::Active => return Ok(true),
                            SessionState::Closing | SessionState::Invalid => {}
                        }
                    }
                    _ => {}
                }
            }
        }
        Ok(false)
    }

    /// Spools until all user sessions are ended then switches to requested mode
    fn mode_change_loop(
        mode: GfxMode,
        devices: DiscreetGpu,
        thread_stop: Arc<AtomicBool>,
        config: Arc<Mutex<GfxConfig>>,
    ) -> Result<String, GfxError> {
        info!("display-manager thread started");
        let no_logind;
        let logout_timeout_s;

        const SLEEP_PERIOD: Duration = Duration::from_millis(100);
        let start_time = Instant::now();

        let connection = Connection::new_system()?;
        let manager = ManagerProxy::new(&connection)?;
        let mut sessions = manager.list_sessions()?;

        loop {
            // Don't wait on logind stuff if set
            if let Ok(config) = config.try_lock() {
                no_logind = config.no_logind;
                logout_timeout_s = config.logout_timeout_s;
                info!("logout_timeout_s = {}", logout_timeout_s);

                if no_logind {
                    info!("no_logind option is set");
                }
                break;
            }
        }

        while !no_logind {
            if thread_stop.load(Ordering::SeqCst) {
                info!("Thread forced to exit");
                return Ok("Exited".to_string());
            }

            let tmp = manager.list_sessions()?;
            if !tmp.iter().eq(&sessions) {
                info!("GFX thread: Sessions list changed");
                sessions = tmp;
            }

            if !Self::graphical_user_sessions_exist(&connection, &sessions)? {
                break;
            }

            // exit if 3 minutes pass
            if logout_timeout_s != 0
                && Instant::now().duration_since(start_time).as_secs() > logout_timeout_s
            {
                let detail = format!("Time ({} seconds) for logout exceeded", logout_timeout_s);
                warn!("{}", detail);
                return Err(GfxError::DisplayManagerTimeout(detail));
            }

            // Don't spin at max speed
            sleep(SLEEP_PERIOD);
        }

        if !no_logind {
            info!("GFX thread: all graphical user sessions ended, continuing");
            Self::do_display_manager_action("stop")?;
            Self::wait_display_manager_state("inactive")?;
        }

        let mut mode_to_save = mode;
        // Need to change to integrated before we can change to vfio or compute
        if let Ok(mut config) = config.try_lock() {
            // Since we have a lock, reset tmp to none. This thread should only ever run
            // for Integrated, Hybrid, or Nvidia. Tmp is also only for informational
            config.tmp_mode = None;
            //
            let vfio_enable = config.vfio_enable;

            // Failsafe. In the event this loop is run with a switch from nvidia in use
            // to vfio or compute do a forced switch to integrated instead to prevent issues
            if matches!(mode, GfxMode::Compute | GfxMode::Vfio)
                && matches!(config.mode, GfxMode::Dedicated | GfxMode::Hybrid)
            {
                Self::do_mode_setup_tasks(GfxMode::Integrated, vfio_enable, &devices)?;
                if !no_logind {
                    Self::do_display_manager_action("restart")?;
                }
                mode_to_save = GfxMode::Integrated;
            } else {
                Self::do_mode_setup_tasks(mode, vfio_enable, &devices)?;
                if !no_logind {
                    Self::do_display_manager_action("restart")?;
                }
            }
        }

        // Save selected mode in case of reboot
        Self::save_gfx_mode(mode_to_save, config);
        info!("GFX thread: display-manager started");

        let v: &str = mode.into();
        info!("GFX thread: Graphics mode changed to {} successfully", v);
        Ok(format!("Graphics mode changed to {} successfully", v))
    }

    /// The thread is used only in cases where a logout is required
    fn setup_mode_change_thread(&mut self, mode: GfxMode) {
        // First, stop all threads
        self.thread_exit.store(true, Ordering::Release);
        // Give threads a chance to read
        std::thread::sleep(Duration::from_secs(1));
        // then reset
        self.thread_exit.store(false, Ordering::Release);

        let config = self.config.clone();
        let devices = self.dgpu.clone();
        let rx = self.thread_exit.clone();

        std::thread::spawn(move || {
            // A thread spawn typically means we're doing a rebootless change, so track pending mode
            if let Ok(mut config) = config.try_lock() {
                config.pending_mode = Some(mode);
            }

            Self::mode_change_loop(mode, devices, rx, config.clone())
                .map_err(|err| {
                    error!("{}", err);
                })
                .ok();

            if let Ok(mut config) = config.try_lock() {
                config.pending_mode = None;
                config.pending_action = None;
            }
        });
    }

    /// Initiates a mode change by starting a thread that will wait until all
    /// graphical sessions are exited before performing the tasks required
    /// to switch modes.
    ///
    /// For manually calling (not on boot/startup) via dbus
    pub fn set_gfx_mode(&mut self, mode: GfxMode) -> Result<GfxRequiredUserAction, GfxError> {
        if has_asus_gsync_gfx_mode() {
            if let Ok(gsync) = get_asus_gsync_gfx_mode() {
                if gsync == 1 {
                    return Err(GfxError::AsusGsyncModeActive);
                }
            }
        }

        if !self.get_supported_modes().contains(&mode) {
            is_gpu_enabled()?;
        }

        let vfio_enable = if let Ok(config) = self.config.try_lock() {
            config.vfio_enable
        } else {
            false
        };

        if !vfio_enable && matches!(mode, GfxMode::Vfio) {
            return Err(GfxError::VfioDisabled);
        }

        mode_support_check(&mode, &self.dgpu)?;

        // determine which method we need here
        let action_required = self.mode_change_action(mode);

        match action_required {
            GfxRequiredUserAction::Logout => {
                info!("mode change requires a logout to complete");
                if let Ok(mut config) = self.config.lock() {
                    config.pending_action = Some(action_required);
                }
                self.setup_mode_change_thread(mode);
            }
            GfxRequiredUserAction::Reboot => {
                info!("mode change requires reboot");
                if let Ok(mut config) = self.config.lock() {
                    if let Some(tmp) = config.tmp_mode {
                        // If they are the same canel the reboot
                        if tmp == config.mode {
                            config.tmp_mode = None;
                            config.pending_action = None;
                        } else {
                            config.tmp_mode = Some(config.mode);
                            config.pending_action = Some(action_required);
                        }
                    } else {
                        config.tmp_mode = Some(config.mode);
                        config.pending_action = Some(action_required);
                    }

                    config.mode = mode;
                    config.write();
                }
            }
            GfxRequiredUserAction::Integrated => {
                info!("mode change requires user to be in Integrated mode first");
            }
            // Generally None for vfio, compute, integrated only
            GfxRequiredUserAction::None => {
                info!("mode change does not require logout");
                Self::do_mode_setup_tasks(mode, vfio_enable, &self.dgpu)?;
                info!("Graphics mode changed to {}", <&str>::from(mode));
                if let Ok(mut config) = self.config.try_lock() {
                    config.tmp_mode = None;
                    config.pending_action = None;
                    config.pending_mode = None;

                    if (matches!(mode, GfxMode::Vfio) && config.vfio_save)
                        || matches!(mode, GfxMode::Compute) && config.compute_save
                    {
                        config.mode = mode;
                        config.write();
                    } else if matches!(mode, GfxMode::Vfio | GfxMode::Compute) {
                        config.tmp_mode = Some(mode);
                    }
                }
            }
        }

        Ok(action_required)
    }
}
07070100000012000081A400000000000000000000000161FDA94900000C31000000000000000000000000000000000000002000000000supergfxctl-4.0.4/src/daemon.rsuse std::{
    env,
    error::Error,
    sync::{Arc, Mutex},
};

use log::{error, info, warn, LevelFilter};
use std::io::Write;
use supergfxctl::{
    config::GfxConfig,
    controller::CtrlGraphics,
    error::GfxError,
    gfx_vendors::GfxMode,
    special_asus::{get_asus_gsync_gfx_mode, has_asus_gsync_gfx_mode},
    CONFIG_PATH, DBUS_DEST_NAME,
};
use zbus::{fdo, Connection, ObjectServer};

pub fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut logger = env_logger::Builder::new();
    logger
        .target(env_logger::Target::Stdout)
        .format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()))
        .filter(None, LevelFilter::Info)
        .init();

    let is_service = match env::var_os("IS_SUPERGFX_SERVICE") {
        Some(val) => val == "1",
        None => false,
    };

    if !is_service {
        println!("supergfxd schould be only run from the right systemd service");
        println!(
            "do not run in your terminal, if you need an logs please use journalctl -b -u supergfxd"
        );
        println!("supergfxd will now exit");
        return Ok(());
    }

    start_daemon()
}

fn start_daemon() -> Result<(), Box<dyn Error>> {
    // Start zbus server
    let connection = Connection::new_system()?;
    fdo::DBusProxy::new(&connection)?.request_name(
        DBUS_DEST_NAME,
        fdo::RequestNameFlags::ReplaceExisting.into(),
    )?;

    let mut object_server = ObjectServer::new(&connection);

    let config = GfxConfig::load(CONFIG_PATH.into());
    let config = Arc::new(Mutex::new(config));

    // Graphics switching requires some checks on boot specifically for g-sync capable laptops
    match CtrlGraphics::new(config.clone()) {
        Ok(mut ctrl) => {
            // Need to check if a laptop has the dedicated gfx switch
            if has_asus_gsync_gfx_mode() {
                do_asus_laptop_checks(&ctrl, config)?;
            } else {
                ctrl.reload()
                    .unwrap_or_else(|err| error!("Gfx controller: {}", err));
            }

            ctrl.add_to_server(&mut object_server);
        }
        Err(err) => {
            error!("Gfx control: {}", err);
        }
    }

    // Loop to check errors and iterate zbus server
    loop {
        if let Err(err) = object_server.try_handle_next() {
            error!("{}", err);
        }
    }
}

fn do_asus_laptop_checks(
    ctrl: &CtrlGraphics,
    config: Arc<Mutex<GfxConfig>>,
) -> Result<(), GfxError> {
    if let Ok(ded) = get_asus_gsync_gfx_mode() {
        if let Ok(config) = config.lock() {
            if ded == 1 {
                warn!("Dedicated GFX toggle is on but driver mode is not nvidia \nSetting to nvidia driver mode");
                let devices = ctrl.dgpu();
                CtrlGraphics::do_mode_setup_tasks(GfxMode::Dedicated, false, &devices)?;
            } else if ded == 0 {
                info!("Dedicated GFX toggle is off");
                let devices = ctrl.dgpu();
                CtrlGraphics::do_mode_setup_tasks(config.mode, false, &devices)?;
            }
        }
    }
    Ok(())
}
07070100000013000081A400000000000000000000000161FDA94900000C7E000000000000000000000000000000000000001F00000000supergfxctl-4.0.4/src/error.rsuse std::fmt;
use std::{error, path::PathBuf, process::ExitStatus};

#[derive(Debug)]
pub enum GfxError {
    ParseVendor,
    DisplayManagerAction(String, ExitStatus),
    DisplayManagerTimeout(String),
    AsusGsyncModeActive,
    VfioBuiltin,
    VfioDisabled,
    MissingModule(String),
    Modprobe(String),
    Command(String, std::io::Error),
    Path(String, std::io::Error),
    Read(String, std::io::Error),
    Write(String, std::io::Error),
    NotSupported(String),
    Io(PathBuf, std::io::Error),
    Zbus(zbus::Error),
    ZbusFdo(zbus::fdo::Error),
}

impl GfxError {
    pub fn from_io(error: std::io::Error, detail: PathBuf) -> Self {
        Self::Io(detail, error)
    }
}

impl fmt::Display for GfxError {
    // This trait requires `fmt` with this exact signature.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            GfxError::ParseVendor => write!(f, "Could not parse vendor name"),
            GfxError::DisplayManagerAction(action, status) => {
                write!(f, "Display-manager action {} failed: {}", action, status)
            }
            GfxError::DisplayManagerTimeout(state) => {
                write!(f, "Timed out waiting for display-manager {} state", state)
            }
            GfxError::AsusGsyncModeActive => write!(
                f,
                "Can not switch gfx modes when dedicated/G-Sync mode is active"
            ),
            GfxError::VfioBuiltin => write!(
                f,
                "Can not switch to vfio mode if the modules are built in to kernel"
            ),
            GfxError::VfioDisabled => {
                write!(f, "Can not switch to vfio mode if disabled in config file")
            }
            GfxError::MissingModule(m) => write!(f, "The module {} is missing", m),
            GfxError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail),
            GfxError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error),
            GfxError::Path(path, error) => write!(f, "Path {}: {}", path, error),
            GfxError::Read(path, error) => write!(f, "Read {}: {}", path, error),
            GfxError::Write(path, error) => write!(f, "Write {}: {}", path, error),
            GfxError::NotSupported(path) => write!(f, "{}", path),
            GfxError::Io(detail, error) => {
                if detail.clone().into_os_string().is_empty() {
                    write!(f, "std::io error: {}", error)
                } else {
                    write!(f, "std::io error: {}, {}", error, detail.display())
                }
            }
            GfxError::Zbus(detail) => write!(f, "Zbus error: {}", detail),
            GfxError::ZbusFdo(detail) => write!(f, "Zbus error: {}", detail),
        }
    }
}

impl error::Error for GfxError {}

impl From<zbus::Error> for GfxError {
    fn from(err: zbus::Error) -> Self {
        GfxError::Zbus(err)
    }
}

impl From<zbus::fdo::Error> for GfxError {
    fn from(err: zbus::fdo::Error) -> Self {
        GfxError::ZbusFdo(err)
    }
}

impl From<std::io::Error> for GfxError {
    fn from(err: std::io::Error) -> Self {
        GfxError::Io(PathBuf::new(), err)
    }
}
07070100000014000081A400000000000000000000000161FDA9490000186A000000000000000000000000000000000000002500000000supergfxctl-4.0.4/src/gfx_devices.rsuse log::{error, info, warn};

use crate::{
    do_driver_action,
    error::GfxError,
    gfx_vendors::{GfxPower, GfxVendor},
    pci_device::{rescan_pci_bus, PciDevice, RuntimePowerManagement},
    special_asus::is_gpu_enabled,
    NVIDIA_DRIVERS,
};

use std::path::PathBuf;

/// Collection of all graphics devices. Functions intend to work on the device
/// determined to be the discreet GPU only.
#[derive(Clone)]
pub struct DiscreetGpu {
    vendor: GfxVendor,
    dgpu_idx: usize,
    functions: Vec<PciDevice>,
}

impl DiscreetGpu {
    pub fn new() -> Result<DiscreetGpu, GfxError> {
        // first need to check asus specific paths
        match is_gpu_enabled() {
            Ok(_) => {}
            Err(e) => {
                warn!("{}", e);
                return Ok(Self {
                    vendor: GfxVendor::Unknown,
                    dgpu_idx: 0,
                    functions: Vec::new(),
                });
            }
        }

        info!("Rescanning PCI bus");
        rescan_pci_bus()?;
        let devs = PciDevice::all()?;

        let functions = |parent: &PciDevice| -> Vec<PciDevice> {
            let mut functions = Vec::new();
            if let Some(parent_slot) = parent.id().split('.').next() {
                for func in devs.iter() {
                    if let Some(func_slot) = func.id().split('.').next() {
                        if func_slot == parent_slot {
                            info!("{}: Function for {}", func.id(), parent.id());
                            functions.push(func.clone());
                        }
                    }
                }
            }
            functions
        };

        for dev in devs.iter() {
            // graphics device class
            if 0x03 == (dev.class()? >> 16) & 0xFF {
                if dev.is_dgpu()? {
                    let vendor = GfxVendor::from(dev.vendor()?);
                    if matches!(
                        vendor,
                        GfxVendor::Nvidia | GfxVendor::Amd | GfxVendor::Intel
                    ) {
                        info!("{} dGPU found", <&str>::from(&vendor));
                        dev.set_runtime_pm(RuntimePowerManagement::Auto)?;

                        let functions = functions(dev);
                        let mut dgpu_idx = 0;
                        for (i, f) in functions.iter().enumerate() {
                            if f.is_dgpu()? {
                                dgpu_idx = i;
                                break;
                            }
                        }

                        return Ok(Self {
                            vendor,
                            dgpu_idx,
                            functions,
                        });
                    }
                }
            }
        }
        Err(GfxError::NotSupported("No dGPU found".to_string()))
    }

    pub fn functions(&self) -> &[PciDevice] {
        &self.functions
    }

    pub fn vendor(&self) -> GfxVendor {
        self.vendor
    }

    pub fn is_nvidia(&self) -> bool {
        self.vendor == GfxVendor::Nvidia
    }

    pub fn is_amd(&self) -> bool {
        self.vendor == GfxVendor::Amd
    }

    pub fn is_intel(&self) -> bool {
        self.vendor == GfxVendor::Intel
    }

    pub fn get_dgpu_runtime_status(&self) -> Result<GfxPower, GfxError> {
        if self.functions.len() > 0 {
            return self.functions[self.dgpu_idx].get_runtime_status();
        }
        Err(GfxError::NotSupported("Could not find dGPU".to_string()))
    }

    pub fn set_runtime_pm(&self, pm: RuntimePowerManagement) -> Result<(), GfxError> {
        self.functions
            .iter()
            .try_for_each(|f| f.set_runtime_pm(pm))
            .map_err(|e| GfxError::from(e))
    }

    pub fn unbind(&self) -> Result<(), GfxError> {
        for func in self.functions.iter() {
            if func.path().exists() {
                match func.driver() {
                    Ok(driver) => {
                        info!("{}: Unbinding {}", driver.id(), func.id());
                        unsafe {
                            driver.unbind(func).map_err(|err| {
                                error!("gfx unbind: {}", err);
                                err
                            })?;
                        }
                    }
                    Err(err) => match err.kind() {
                        std::io::ErrorKind::NotFound => (),
                        _ => {
                            error!("gfx driver: {:?}, {}", func.path(), err);
                            return Err(GfxError::from_io(err, PathBuf::from(func.path())));
                        }
                    },
                }
            }
        }
        Ok(())
    }

    pub fn remove(&self) -> Result<(), GfxError> {
        for func in self.functions.iter() {
            if func.path().exists() {
                match func.driver() {
                    Ok(driver) => {
                        error!("{}: in use by {}", func.id(), driver.id());
                    }
                    Err(why) => match why.kind() {
                        std::io::ErrorKind::NotFound => {
                            info!("{}: Removing", func.id());
                            unsafe {
                                // ignore errors and carry on
                                if let Err(err) = func.remove() {
                                    error!("gfx remove: {}", err);
                                }
                            }
                        }
                        _ => {
                            error!("Remove device failed");
                        }
                    },
                }
            } else {
                warn!("{}: Already removed", func.id());
            }
        }
        info!("Removed all gfx devices");
        Ok(())
    }

    pub fn unbind_remove(&self) -> Result<(), GfxError> {
        self.unbind()?;
        self.remove()
    }

    pub fn do_driver_action(&self, action: &str) -> Result<(), GfxError> {
        if self.is_nvidia() {
            for driver in NVIDIA_DRIVERS.iter() {
                do_driver_action(driver, action)?;
            }
        }
        Ok(())
    }
}
07070100000015000081A400000000000000000000000161FDA94900000DDB000000000000000000000000000000000000002500000000supergfxctl-4.0.4/src/gfx_vendors.rsuse serde_derive::{Deserialize, Serialize};
use std::str::FromStr;
use zvariant_derive::Type;

use crate::error::GfxError;

#[derive(Debug, Type, PartialEq, Copy, Clone, Deserialize, Serialize)]
pub enum GfxPower {
    Active,
    Suspended,
    Off,
    Unknown,
}

impl FromStr for GfxPower {
    type Err = GfxError;

    fn from_str(s: &str) -> Result<Self, GfxError> {
        match s.to_lowercase().trim() {
            "active" => Ok(GfxPower::Active),
            "suspended" => Ok(GfxPower::Suspended),
            "off" => Ok(GfxPower::Off),
            _ => Ok(GfxPower::Unknown),
        }
    }
}

impl From<&GfxPower> for &str {
    fn from(gfx: &GfxPower) -> &'static str {
        match gfx {
            GfxPower::Active => "active",
            GfxPower::Suspended => "suspended",
            GfxPower::Off => "off",
            GfxPower::Unknown => "unknown",
        }
    }
}

#[derive(Debug, Type, PartialEq, Copy, Clone, Deserialize, Serialize)]
pub enum GfxVendor {
    Nvidia,
    Amd,
    Intel,
    Unknown,
}

impl From<u16> for GfxVendor {
    fn from(vendor: u16) -> Self {
        match vendor {
            0x1002 => GfxVendor::Amd,
            0x10DE => GfxVendor::Nvidia,
            0x8086 => GfxVendor::Intel,
            _ => GfxVendor::Unknown,
        }
    }
}

impl From<GfxVendor> for &str {
    fn from(vendor: GfxVendor) -> Self {
        match vendor {
            GfxVendor::Nvidia => "Nvidia",
            GfxVendor::Amd => "AMD",
            GfxVendor::Intel => "Intel",
            GfxVendor::Unknown => "Unknown",
        }
    }
}

impl From<&GfxVendor> for &str {
    fn from(vendor: &GfxVendor) -> Self {
        <&str>::from(*vendor)
    }
}

#[derive(Debug, Type, PartialEq, Copy, Clone, Deserialize, Serialize)]
pub enum GfxMode {
    Hybrid,
    Dedicated,
    Integrated,
    Compute,
    Vfio,
    Egpu,
    None,
}

impl FromStr for GfxMode {
    type Err = GfxError;

    fn from_str(s: &str) -> Result<Self, GfxError> {
        match s.to_lowercase().trim() {
            "hybrid" => Ok(GfxMode::Hybrid),
            "dedicated" => Ok(GfxMode::Dedicated),
            "integrated" => Ok(GfxMode::Integrated),
            "compute" => Ok(GfxMode::Compute),
            "vfio" => Ok(GfxMode::Vfio),
            "egpu" => Ok(GfxMode::Egpu),
            _ => Err(GfxError::ParseVendor),
        }
    }
}

impl From<GfxMode> for &str {
    fn from(gfx: GfxMode) -> &'static str {
        match gfx {
            GfxMode::Hybrid => "hybrid",
            GfxMode::Dedicated => "dedicated",
            GfxMode::Integrated => "integrated",
            GfxMode::Compute => "compute",
            GfxMode::Vfio => "vfio",
            GfxMode::Egpu => "egpu",
            GfxMode::None => "none",
        }
    }
}

impl From<&GfxMode> for &str {
    fn from(gfx: &GfxMode) -> &'static str {
        (*gfx).into()
    }
}

#[derive(Debug, Type, PartialEq, Copy, Clone, Deserialize, Serialize)]
pub enum GfxRequiredUserAction {
    Logout,
    Reboot,
    Integrated,
    None,
}

impl From<GfxRequiredUserAction> for &str {
    fn from(gfx: GfxRequiredUserAction) -> &'static str {
        match gfx {
            GfxRequiredUserAction::Logout => "logout",
            GfxRequiredUserAction::Reboot => "reboot",
            GfxRequiredUserAction::Integrated => "switch to integrated first",
            GfxRequiredUserAction::None => "none",
        }
    }
}

impl From<&GfxRequiredUserAction> for &str {
    fn from(gfx: &GfxRequiredUserAction) -> &'static str {
        (*gfx).into()
    }
}
07070100000016000081A400000000000000000000000161FDA94900001C58000000000000000000000000000000000000001D00000000supergfxctl-4.0.4/src/lib.rsuse std::{fs::OpenOptions, io::Read, path::Path, process::Command, str::FromStr};

use gfx_devices::DiscreetGpu;
use gfx_vendors::GfxMode;
use log::{error, info, warn};

use crate::{error::GfxError, special_asus::asus_egpu_exists};

/// The configuration for graphics. This should be saved and loaded on boot.
pub mod config;
mod config_old;
/// Control functions for setting graphics.
pub mod controller;
/// Error: 404
pub mod error;
/// Mode names, follows what distros defined as common.
pub mod gfx_vendors;
/// Special-case functions for check/read/write of key functions on unique laptops
/// such as the G-Sync mode available on some ASUS ROG laptops
pub mod special_asus;

/// Defined DBUS Interface for supergfxctl
pub mod zbus_iface;
/// Defined DBUS Proxy for supergfxctl
pub mod zbus_proxy;

pub(crate) mod gfx_devices;
/// System interface helpers.
pub(crate) mod pci_device;

/// Helper to expose the current crate version to external code
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
/// Generic path that is used to save the daemon config state
pub const CONFIG_PATH: &str = "/etc/supergfxd.conf";
/// Destination name to be used in the daemon when setting up DBUS connection
pub const DBUS_DEST_NAME: &str = "org.supergfxctl.Daemon";
/// Interface path name. Should be common across daemon and client.
pub const DBUS_IFACE_PATH: &str = "/org/supergfxctl/Gfx";

pub const KERNEL_CMDLINE: &str = "/proc/cmdline";

const NVIDIA_DRIVERS: [&str; 4] = ["nvidia_drm", "nvidia_modeset", "nvidia_uvm", "nvidia"];

const VFIO_DRIVERS: [&str; 6] = [
    "vfio_pci",
    "vfio_pci_core",
    "vfio_iommu_type1",
    "vfio_virqfd",
    "vfio_mdev",
    "vfio",
];

const DISPLAY_MANAGER: &str = "display-manager.service";

const MODPROBE_PATH: &str = "/etc/modprobe.d/supergfxd.conf";

static MODPROBE_NVIDIA_BASE: &[u8] = br#"# Automatically generated by supergfxd
blacklist nouveau
alias nouveau off
options nvidia NVreg_DynamicPowerManagement=0x02
"#;

static MODPROBE_NVIDIA_DRM_MODESET: &[u8] = br#"
options nvidia-drm modeset=1
"#;

static MODPROBE_INTEGRATED: &[u8] = br#"# Automatically generated by supergfxd
blacklist i2c_nvidia_gpu
blacklist nvidia
blacklist nvidia-drm
blacklist nvidia-modeset
blacklist nouveau
alias nouveau off
"#;

static MODPROBE_VFIO: &[u8] = br#"options vfio-pci ids="#;

const XORG_FILE: &str = "90-nvidia-primary.conf";
const XORG_PATH: &str = "/etc/X11/xorg.conf.d/";

static PRIMARY_GPU_AMD_BEGIN: &[u8] = br#"# Automatically generated by supergfxd
Section "OutputClass"
        Identifier "amdgpu"
        MatchDriver "amdgpu"
        Driver "amdgpu""#;

static PRIMARY_GPU_NVIDIA_BEGIN: &[u8] = br#"# Automatically generated by supergfxd
Section "OutputClass"
    Identifier "nvidia"
    MatchDriver "nvidia-drm"
    Driver "nvidia"
    Option "AllowEmptyInitialConfiguration" "true"
    Option "AllowExternalGpus" "true""#;

static PRIMARY_IS_EGPU: &[u8] = br#"
    Option "AllowExternalGpus" "true""#;

static PRIMARY_IS_DGPU: &[u8] = br#"
    Option "PrimaryGPU" "true""#;

static PRIMARY_GPU_END: &[u8] = br#"
EndSection"#;

/// Basic check for support. If `()` returned everything is kosher.
fn mode_support_check(mode: &GfxMode, dgpu: &DiscreetGpu) -> Result<(), GfxError> {
    if matches!(mode, GfxMode::Egpu) && !asus_egpu_exists() {
        let text = "Egpu mode requested when either the laptop doesn't support it or the kernel is not recent enough".to_string();
        return Err(GfxError::NotSupported(text));
    }

    if matches!(mode, GfxMode::Dedicated) && dgpu.is_amd() {
        let text = "Dedicated mode unsupported on AMD dGPU systems".to_string();
        return Err(GfxError::NotSupported(text));
    }

    if matches!(mode, GfxMode::Compute) && dgpu.is_amd() {
        let text = "Compute mode unsupported on AMD dGPU systems".to_string();
        return Err(GfxError::NotSupported(text));
    }

    Ok(())
}

/// Add or remove driver modules
fn do_driver_action(driver: &str, action: &str) -> Result<(), GfxError> {
    let mut cmd = Command::new(action);
    cmd.arg(driver);

    let mut count = 0;
    const MAX_TRIES: i32 = 6;
    loop {
        if count > MAX_TRIES {
            let msg = format!("{} {} failed for unknown reason", action, driver);
            error!("{}", msg);
            return Ok(()); //Err(GfxError::Modprobe(msg));
        }

        let output = cmd
            .output()
            .map_err(|err| GfxError::Command(format!("{:?}", cmd), err))?;
        if !output.status.success() {
            if output
                .stderr
                .ends_with("is not currently loaded\n".as_bytes())
            {
                return Ok(());
            }
            if output.stderr.ends_with("is builtin.\n".as_bytes()) {
                return Err(GfxError::VfioBuiltin);
            }
            if output.stderr.ends_with("Permission denied\n".as_bytes()) {
                warn!(
                    "{} {} failed: {:?}",
                    action,
                    driver,
                    String::from_utf8_lossy(&output.stderr)
                );
                warn!("It may be safe to ignore the above error, run `lsmod |grep {}` to confirm modules loaded", driver);
                return Ok(());
            }
            if String::from_utf8_lossy(&output.stderr)
                .contains(&format!("Module {} not found", driver))
            {
                return Err(GfxError::MissingModule(driver.into()));
            }
            if count >= MAX_TRIES {
                let msg = format!(
                    "{} {} failed: {:?}",
                    action,
                    driver,
                    String::from_utf8_lossy(&output.stderr)
                );
                return Err(GfxError::Modprobe(msg));
            }
        } else if output.status.success() {
            return Ok(());
        }

        count += 1;
        std::thread::sleep(std::time::Duration::from_millis(50));
    }
}

pub fn nvidia_drm_modeset() -> Result<bool, GfxError> {
    let path = Path::new(KERNEL_CMDLINE);
    let mut file = OpenOptions::new()
        .read(true)
        .open(path)
        .map_err(|err| GfxError::Path(KERNEL_CMDLINE.to_string(), err))?;
    let mut buf = String::new();
    file.read_to_string(&mut buf)?;

    // No need to be fast here, just check and go
    if buf.contains("nvidia-drm.modeset=0") {
        return Ok(false);
    } else if buf.contains("nvidia-drm.modeset=1") {
        return Ok(true);
    }
    warn!("nvidia-drm.modeset is no set for kernel cmdline");
    Ok(false)
}

pub fn get_kernel_cmdline_mode() -> Result<Option<GfxMode>, GfxError> {
    let path = Path::new(KERNEL_CMDLINE);
    let mut file = OpenOptions::new()
        .read(true)
        .open(path)
        .map_err(|err| GfxError::Path(KERNEL_CMDLINE.to_string(), err))?;
    let mut buf = String::new();
    file.read_to_string(&mut buf)?;

    // No need to be fast here, just check and go
    for cmd in buf.split(' ') {
        if cmd.contains("supergfxd.mode=") {
            let mode = cmd.trim_start_matches("supergfxd.mode=");
            let mode = GfxMode::from_str(mode)?;
            return Ok(Some(mode));
        }
    }

    info!("supergfxd.mode not set, ignoring");
    Ok(None)
}
07070100000017000081A400000000000000000000000161FDA94900001CCC000000000000000000000000000000000000002400000000supergfxctl-4.0.4/src/pci_device.rsuse std::fs;
use std::io;
use std::io::Read;
use std::io::Write;
use std::process::Command;
use std::str::FromStr;
use std::{
    fs::write,
    path::{Path, PathBuf},
};

use crate::error::GfxError;
use crate::gfx_vendors::GfxPower;

const PCI_BUS_PATH: &str = "/sys/bus/pci";
const PM_CONTROL_PATH: &str = "power/control";
const PM_RUNTIME_STATUS_PATH: &str = "power/runtime_status";

/// Will rescan the device tree, which adds all removed devices back
pub fn rescan_pci_bus() -> Result<(), GfxError> {
    let path = PathBuf::from(PCI_BUS_PATH).join("rescan");
    write(&path, "1").map_err(|e| GfxError::from_io(e, path))
}

#[derive(Clone)]
pub struct PciDriver {
    path: PathBuf,
}

impl PciDriver {
    /// Return the id of the sys object
    pub fn id(&self) -> &str {
        self.path
            .file_name()
            .unwrap() // A valid path does not end in .., so safe
            .to_str()
            .unwrap() // A valid path must be valid UTF-8, so safe
    }

    /// Write a file underneath the sys object
    fn write_file<P: AsRef<Path>, S: AsRef<[u8]>>(&self, name: P, data: S) -> Result<(), GfxError> {
        let path = self.path.join(name.as_ref());
        let mut file = fs::OpenOptions::new()
            .write(true)
            .open(&path)
            .map_err(|e| GfxError::from_io(e, path.clone()))?;
        file.write_all(data.as_ref())
            .map_err(|e| GfxError::from_io(e, path))?;

        Ok(())
    }

    pub unsafe fn bind(&self, device: &PciDevice) -> Result<(), GfxError> {
        self.write_file("bind", device.id())
    }

    pub unsafe fn unbind(&self, device: &PciDevice) -> Result<(), GfxError> {
        self.write_file("unbind", device.id())
    }
}

macro_rules! pci_devices {
    ($( fn $file:tt -> $out:tt; )*) => {
        $(
            pub fn $file(&self) -> Result<$out, GfxError> {
                let v = self.read_file(stringify!($file))?;
                $out::from_str_radix(v[2..].trim(), 16).map_err(|err| {
                    io::Error::new(
                        io::ErrorKind::InvalidData,
                        format!("{}/{}: {}", self.path().display(), stringify!($file), err)
                    ).into()
                })
            }
        )*
    }
}

#[derive(Clone)]
pub struct PciDevice {
    path: PathBuf,
}

impl PciDevice {
    /// Retrieve all of the object instances of a sys class
    pub fn all() -> Result<Vec<Self>, GfxError> {
        let mut ret = Vec::new();
        let path = PathBuf::from(PCI_BUS_PATH).join("devices");
        for entry_res in fs::read_dir(&path).map_err(|e| GfxError::from_io(e, path.clone()))? {
            let entry = entry_res.map_err(|e| GfxError::from_io(e, path.clone()))?;
            if entry.path().is_dir() {
                ret.push(Self { path: entry.path() });
            }
        }

        Ok(ret)
    }

    /// Return the id of the sys object
    pub fn id(&self) -> &str {
        self.path()
            .file_name()
            .unwrap() // A valid path does not end in .., so safe
            .to_str()
            .unwrap() // A valid path must be valid UTF-8, so safe
    }

    pub fn path(&self) -> &Path {
        &self.path
    }

    pub fn owned_path(&self) -> PathBuf {
        self.path.clone()
    }

    /// Read a file underneath the sys object
    fn read_file<P: AsRef<Path>>(&self, name: P) -> Result<String, GfxError> {
        let mut data = String::new();
        let path = self.path().join(name.as_ref());
        let mut file = fs::OpenOptions::new()
            .read(true)
            .open(&path)
            .map_err(|e| GfxError::from_io(e, path.clone()))?;
        file.read_to_string(&mut data)
            .map_err(|e| GfxError::from_io(e, path))?;

        Ok(data)
    }

    /// Write a file underneath the sys object
    fn write_file<P: AsRef<Path>, S: AsRef<[u8]>>(&self, name: P, data: S) -> Result<(), GfxError> {
        let path = self.path().join(name.as_ref());
        let mut file = fs::OpenOptions::new()
            .write(true)
            .open(&path)
            .map_err(|e| GfxError::from_io(e, path.clone()))?;
        file.write_all(data.as_ref())
            .map_err(|e| GfxError::from_io(e, path))?;

        Ok(())
    }

    pci_devices! {
        fn class -> u32;
        fn device -> u16;
        fn vendor -> u16;
    }

    fn lscpi(&self) -> Result<String, GfxError> {
        let code = format!("{:#01X}:{:#01X}", self.vendor()?, self.device()?);
        let mut cmd = Command::new("lspci");
        cmd.args(["-d", &code]);
        let s = String::from_utf8_lossy(&cmd.output()?.stdout).into_owned();
        Ok(s)
    }

    pub fn lscpi_amd_check(&self) -> Result<bool, GfxError> {
        let s = self.lscpi()?;
        for pat in ["Radeon RX"] {
            if s.contains(pat) {
                return Ok(true);
            }
        }
        Ok(false)
    }

    pub fn lscpi_nvidia_check(&self) -> Result<bool, GfxError> {
        let s = self.lscpi()?;
        for pat in ["GeForce"] {
            if s.contains(pat) {
                return Ok(true);
            }
        }
        Ok(false)
    }

    pub fn is_dgpu(&self) -> Result<bool, GfxError> {
        // The non-boot GPU is the dGPU
        match self.read_file("boot_vga") {
            Ok(n) => {
                if n.trim() == "0" {
                    return Ok(true);
                } else {
                    return Ok(false);
                }
            }
            Err(_) => {
                if self.lscpi_nvidia_check()? | self.lscpi_amd_check()? {
                    return Ok(true);
                }
            }
        }
        Ok(false)
    }

    pub fn driver(&self) -> io::Result<PciDriver> {
        fs::canonicalize(self.path.join("driver")).map(|path| PciDriver { path })
    }

    pub unsafe fn remove(&self) -> Result<(), GfxError> {
        self.write_file("remove", "1")
    }

    pub fn set_runtime_pm(&self, state: RuntimePowerManagement) -> Result<(), GfxError> {
        self.write_file(PM_CONTROL_PATH, <&'static str>::from(state))
    }

    pub fn get_runtime_status(&self) -> Result<GfxPower, GfxError> {
        match self.read_file(PM_RUNTIME_STATUS_PATH) {
            Ok(inner) => GfxPower::from_str(inner.as_str()),
            Err(_) => {
                // if let Some(er) = inner.raw_os_error() {
                //     if er != 2 {
                //         return Err(GfxError::from_io(inner));
                //     }
                // }
                Ok(GfxPower::Off)
            }
        }
    }
}

/// Control whether a device uses, or does not use, runtime power management.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum RuntimePowerManagement {
    Auto,
    On,
    Off,
}

impl From<RuntimePowerManagement> for &'static str {
    fn from(pm: RuntimePowerManagement) -> &'static str {
        match pm {
            RuntimePowerManagement::Auto => "auto",
            RuntimePowerManagement::On => "on",
            RuntimePowerManagement::Off => "off",
        }
    }
}

impl From<&str> for RuntimePowerManagement {
    fn from(pm: &str) -> RuntimePowerManagement {
        match pm {
            "auto" => RuntimePowerManagement::Auto,
            "on" => RuntimePowerManagement::On,
            "off" => RuntimePowerManagement::Off,
            _ => RuntimePowerManagement::On,
        }
    }
}
07070100000018000081A400000000000000000000000161FDA94900000E13000000000000000000000000000000000000002600000000supergfxctl-4.0.4/src/special_asus.rsuse std::{
    fs::OpenOptions,
    io::{Read, Write},
    path::Path,
};

use log::info;

use crate::{do_driver_action, error::GfxError, pci_device::rescan_pci_bus, NVIDIA_DRIVERS};

static ASUS_DGPU_DISABLE_PATH: &str = "/sys/devices/platform/asus-nb-wmi/dgpu_disable";
static ASUS_EGPU_ENABLE_PATH: &str = "/sys/devices/platform/asus-nb-wmi/egpu_enable";

static ASUS_SWITCH_GRAPHIC_MODE: &str =
    "/sys/firmware/efi/efivars/AsusSwitchGraphicMode-607005d5-3f75-4b2e-98f0-85ba66797a3e";

pub fn has_asus_gsync_gfx_mode() -> bool {
    Path::new(ASUS_SWITCH_GRAPHIC_MODE).exists()
}

pub fn get_asus_gsync_gfx_mode() -> Result<i8, GfxError> {
    let path = ASUS_SWITCH_GRAPHIC_MODE;
    let mut file = OpenOptions::new()
        .read(true)
        .open(path)
        .map_err(|err| GfxError::Path(path.into(), err))?;

    let mut data = Vec::new();
    file.read_to_end(&mut data)
        .map_err(|err| GfxError::Read(path.into(), err))?;

    let idx = data.len() - 1;
    Ok(data[idx] as i8)
}

pub(crate) fn asus_dgpu_exists() -> bool {
    if Path::new(ASUS_DGPU_DISABLE_PATH).exists() {
        return true;
    }
    false
}

pub(crate) fn asus_dgpu_disabled() -> Result<bool, GfxError> {
    let path = Path::new(ASUS_DGPU_DISABLE_PATH);
    let mut file = OpenOptions::new()
        .read(true)
        .open(path)
        .map_err(|err| GfxError::Path(ASUS_DGPU_DISABLE_PATH.to_string(), err))?;
    let mut buf = String::new();
    file.read_to_string(&mut buf)?;
    if buf.contains('1') {
        return Ok(true);
    }
    Ok(false)
}

pub(crate) fn asus_egpu_exists() -> bool {
    if Path::new(ASUS_EGPU_ENABLE_PATH).exists() {
        return true;
    }
    false
}

pub(crate) fn asus_egpu_enabled() -> Result<bool, GfxError> {
    let path = Path::new(ASUS_EGPU_ENABLE_PATH);
    let mut file = OpenOptions::new()
        .read(true)
        .open(path)
        .map_err(|err| GfxError::Path(ASUS_EGPU_ENABLE_PATH.to_string(), err))?;
    let mut buf = String::new();
    file.read_to_string(&mut buf)?;
    if buf.contains('1') {
        return Ok(true);
    }
    Ok(false)
}

pub(crate) fn asus_egpu_set_status(status: bool) -> Result<(), GfxError> {
    // toggling from egpu must have the nvidia driver unloaded
    for driver in NVIDIA_DRIVERS.iter() {
        do_driver_action(driver, "rmmod")?;
    }
    // Need to set, scan, set to ensure mode is correctly set
    asus_egpu_toggle(status)?;
    rescan_pci_bus()?;
    asus_egpu_toggle(status)?;
    Ok(())
}

fn asus_egpu_toggle(status: bool) -> Result<(), GfxError> {
    let path = Path::new(ASUS_EGPU_ENABLE_PATH);
    let mut file = OpenOptions::new()
        .write(true)
        .open(path)
        .map_err(|err| GfxError::Path(ASUS_EGPU_ENABLE_PATH.to_string(), err))?;
    let status = if status { 1 } else { 0 };
    file.write_all(status.to_string().as_bytes())
        .map_err(|err| GfxError::Write(ASUS_EGPU_ENABLE_PATH.to_string(), err))?;
    Ok(())
}

pub(crate) fn is_gpu_enabled() -> Result<bool, GfxError> {
    if asus_dgpu_exists() {
        if asus_dgpu_disabled()? {
            if asus_egpu_exists() {
                if !asus_egpu_enabled()? {
                    return Err(GfxError::NotSupported(
                        "ASUS dGPU and eGPU disabled".to_string(),
                    ));
                } else {
                    info!("ASUS eGPU enabled");
                }
            } else {
                return Err(GfxError::NotSupported("ASUS dGPU disabled".to_string()));
            }
        } else {
            info!("ASUS dGPU enabled");
        }
    }
    Ok(true)
}
07070100000019000081A400000000000000000000000161FDA9490000135D000000000000000000000000000000000000002400000000supergfxctl-4.0.4/src/zbus_iface.rsuse ::zbus::dbus_interface;
use log::{error, info, warn};
use zvariant::ObjectPath;

use crate::{
    config::GfxConfigDbus,
    gfx_vendors::{GfxMode, GfxPower, GfxRequiredUserAction},
    DBUS_IFACE_PATH, VERSION,
};

use super::controller::CtrlGraphics;

#[dbus_interface(name = "org.supergfxctl.Daemon")]
impl CtrlGraphics {
    /// Get supergfxd version
    fn version(&self) -> zbus::fdo::Result<String> {
        Ok(VERSION.to_string())
    }

    /// Get the current graphics mode:
    /// enum {
    ///     Hybrid,
    ///     Dedicated,
    ///     Integrated,
    ///     Compute,
    ///     Vfio,
    ///     Egpu,
    /// }
    fn mode(&self) -> zbus::fdo::Result<GfxMode> {
        self.get_gfx_mode().map_err(|err| {
            error!("{}", err);
            zbus::fdo::Error::Failed(format!("GFX fail: {}", err))
        })
    }

    /// Get list of supported modes
    fn supported(&self) -> zbus::fdo::Result<Vec<GfxMode>> {
        Ok(self.get_supported_modes())
    }

    /// Get the vendor nae of the dGPU
    fn vendor(&self) -> zbus::fdo::Result<String> {
        Ok(<&str>::from(self.get_gfx_vendor()).to_string())
    }

    /// Get the current power status:
    /// enum {
    ///     Active,
    ///     Suspended,
    ///     Off,
    ///     Unknown,
    /// }
    fn power(&self) -> zbus::fdo::Result<GfxPower> {
        self.dgpu.get_dgpu_runtime_status().map_err(|err| {
            error!("{}", err);
            zbus::fdo::Error::Failed(format!("GFX fail: {}", err))
        })
    }

    /// Set the graphics mode:
    /// enum {
    ///     Hybrid,
    ///     Dedicated,
    ///     Integrated,
    ///     Compute,
    ///     Vfio,
    ///     Egpu,
    /// }
    ///
    /// Returns action required:
    /// enum {
    ///     Logout,
    ///     Reboot,
    ///     Integrated,
    ///     None,
    /// }
    fn set_mode(&mut self, mode: GfxMode) -> zbus::fdo::Result<GfxRequiredUserAction> {
        info!("Switching gfx mode to {}", <&str>::from(mode));
        let msg = self.set_gfx_mode(mode).map_err(|err| {
            error!("{}", err);
            zbus::fdo::Error::Failed(format!("GFX fail: {}", err))
        })?;

        self.notify_action(&msg)
            .unwrap_or_else(|err| warn!("{}", err));

        self.notify_gfx(&mode)
            .unwrap_or_else(|err| warn!("{}", err));

        Ok(msg)
    }

    /// Get the `String` name of the pending mode change if any
    fn pending_mode(&self) -> zbus::fdo::Result<GfxMode> {
        Ok(self.get_pending_mode())
    }

    /// Get the `String` name of the pending required user action if any
    fn pending_user_action(&self) -> zbus::fdo::Result<GfxRequiredUserAction> {
        Ok(self.get_pending_user_action())
    }

    /// Get the base config, args in order are:
    /// pub mode: GfxMode,
    /// vfio_enable: bool,
    /// vfio_save: bool,
    /// compute_save: bool,
    /// always_reboot: bool,
    /// no_logind: bool,
    /// logout_timeout_s: u64,
    fn config(&self) -> zbus::fdo::Result<GfxConfigDbus> {
        let cfg = self
            .config
            .try_lock()
            .map_err(|e| zbus::fdo::Error::Failed(e.to_string()))?;
        let cfg = GfxConfigDbus::from(&*cfg);
        Ok(cfg)
    }

    /// Set the base config, args in order are:
    /// pub mode: GfxMode,
    /// vfio_enable: bool,
    /// vfio_save: bool,
    /// compute_save: bool,
    /// always_reboot: bool,
    /// no_logind: bool,
    /// logout_timeout_s: u64,
    fn set_config(&mut self, config: GfxConfigDbus) -> zbus::fdo::Result<()> {
        let do_mode_change;
        let mode;

        {
            let mut cfg = self
                .config
                .try_lock()
                .map_err(|e| zbus::fdo::Error::Failed(e.to_string()))?;

            do_mode_change = cfg.mode == config.mode;
            mode = cfg.mode;

            cfg.vfio_enable = config.vfio_enable;
            cfg.vfio_save = config.vfio_save;
            cfg.compute_save = config.compute_save;
            cfg.always_reboot = config.always_reboot;
            cfg.no_logind = config.no_logind;
            cfg.logout_timeout_s = config.logout_timeout_s;
        }

        if do_mode_change {
            self.set_mode(mode).ok();
        }

        Ok(())
    }

    /// Recieve a notification if the graphics mode changes and to which mode
    #[dbus_interface(signal)]
    fn notify_gfx(&self, vendor: &GfxMode) -> zbus::Result<()> {}

    /// Recieve a notification on required action if mode changes
    #[dbus_interface(signal)]
    fn notify_action(&self, action: &GfxRequiredUserAction) -> zbus::Result<()> {}
}

impl CtrlGraphics {
    pub fn add_to_server(self, server: &mut zbus::ObjectServer) {
        server
            .at(&ObjectPath::from_str_unchecked(DBUS_IFACE_PATH), self)
            .map_err(|err| {
                warn!("CtrlGraphics: add_to_server {}", err);
                err
            })
            .ok();
    }
}
0707010000001A000081A400000000000000000000000161FDA94900001155000000000000000000000000000000000000002400000000supergfxctl-4.0.4/src/zbus_proxy.rs//! # DBus interface proxy for: `org.asuslinux.Gfx`
//!
//! This code was generated by `zbus-xmlgen` `1.0.0` from DBus introspection data.
//! Source: `Interface '/org/supergfxctl/Gfx' from service 'org.asuslinux.Daemon' on system bus`.
//!
//! You may prefer to adapt it, instead of using it verbatim.
//!
//! More information can be found in the
//! [Writing a client proxy](https://zeenix.pages.freedesktop.org/zbus/client.html)
//! section of the zbus documentation.
//!
//! This DBus object implements
//! [standard DBus interfaces](https://dbus.freedesktop.org/doc/dbus-specification.html),
//! (`org.freedesktop.DBus.*`) for which the following zbus proxies can be used:
//!
//! * [`zbus::fdo::PropertiesProxy`]
//! * [`zbus::fdo::IntrospectableProxy`]
//! * [`zbus::fdo::PeerProxy`]
//!
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.

use std::sync::mpsc::Sender;

use zbus::{dbus_proxy, Connection, Message, Result};

use crate::{
    gfx_vendors::{GfxMode, GfxPower, GfxRequiredUserAction},
    DBUS_IFACE_PATH,
};

#[dbus_proxy(interface = "org.supergfxctl.Daemon")]
trait Daemon {
    /// Version method
    fn version(&self) -> zbus::Result<String>;

    /// Power method
    fn power(&self) -> zbus::Result<GfxPower>;

    /// SetMode method
    fn set_mode(&self, mode: &GfxMode) -> zbus::Result<GfxRequiredUserAction>;

    /// Get the `String` name of the pending mode change if any
    fn pending_mode(&self) -> zbus::Result<GfxMode>;

    /// Get the `String` name of the pending required user action if any
    fn pending_user_action(&self) -> zbus::Result<GfxRequiredUserAction>;

    /// Mode method
    fn mode(&self) -> zbus::Result<GfxMode>;

    fn supported(&self) -> zbus::Result<Vec<GfxMode>>;

    /// Vendor method
    fn vendor(&self) -> zbus::Result<String>;

    /// NotifyAction signal
    #[dbus_proxy(signal)]
    fn notify_action(&self, action: GfxRequiredUserAction) -> zbus::Result<()>;

    /// NotifyGfx signal
    #[dbus_proxy(signal)]
    fn notify_gfx(&self, mode: GfxMode) -> zbus::Result<()>;
}

pub struct GfxProxy<'a>(pub DaemonProxy<'a>);

impl<'a> GfxProxy<'a> {
    #[inline]
    pub fn new(conn: &Connection) -> Result<Self> {
        let proxy = DaemonProxy::new_for(conn, "org.supergfxctl.Daemon", DBUS_IFACE_PATH)?;
        Ok(GfxProxy(proxy))
    }

    #[inline]
    pub fn new_for(conn: &Connection, destination: &'a str, path: &'a str) -> Result<Self> {
        let proxy = DaemonProxy::new_for(conn, destination, path)?;
        Ok(GfxProxy(proxy))
    }

    #[inline]
    pub fn new_for_owned(conn: Connection, destination: String, path: String) -> Result<Self> {
        let proxy = DaemonProxy::new_for_owned(conn, destination, path)?;
        Ok(GfxProxy(proxy))
    }

    #[inline]
    pub fn proxy(&self) -> &DaemonProxy<'a> {
        &self.0
    }

    #[inline]
    pub fn get_version(&self) -> Result<String> {
        self.0.version()
    }

    #[inline]
    pub fn get_vendor(&self) -> Result<String> {
        self.0.vendor()
    }

    #[inline]
    pub fn get_pwr(&self) -> Result<GfxPower> {
        self.0.power()
    }

    #[inline]
    pub fn get_mode(&self) -> Result<GfxMode> {
        self.0.mode()
    }

    #[inline]
    pub fn get_supported_modes(&self) -> Result<Vec<GfxMode>> {
        self.0.supported()
    }

    #[inline]
    pub fn pending_mode(&self) -> Result<GfxMode> {
        self.0.pending_mode()
    }

    #[inline]
    pub fn pending_user_action(&self) -> Result<GfxRequiredUserAction> {
        self.0.pending_user_action()
    }

    #[inline]
    pub fn write_mode(&self, mode: &GfxMode) -> Result<GfxRequiredUserAction> {
        self.0.set_mode(mode)
    }

    #[inline]
    pub fn connect_notify_action(
        &self,
        send: Sender<GfxRequiredUserAction>,
    ) -> zbus::fdo::Result<()> {
        self.0.connect_notify_action(move |data| {
            send.send(data)
                .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
            Ok(())
        })
    }

    #[inline]
    pub fn connect_notify_gfx(&self, send: Sender<GfxMode>) -> zbus::fdo::Result<()> {
        self.0.connect_notify_gfx(move |data| {
            send.send(data)
                .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
            Ok(())
        })
    }

    #[inline]
    pub fn next_signal(&self) -> Result<Option<Message>> {
        self.0.next_signal()
    }
}
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!263 blocks
openSUSE Build Service is sponsored by