mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-01-22 09:23:19 +01:00
Remove supergfxctl to own repo
This commit is contained in:
@@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
+ `BREAKING:` plain `Image` with time period is changed and old anime configs break as a result (sorry)
|
+ `BREAKING:` plain `Image` with time period is changed and old anime configs break as a result (sorry)
|
||||||
### BREAKING CHANGES
|
### BREAKING CHANGES
|
||||||
- Graphics control:
|
- Graphics control:
|
||||||
+ graphics control is pulled out of asusd and moved to new crate; supergfxctl
|
+ graphics control is pulled out of asusd and moved to new package; https://gitlab.com/asus-linux/supergfxctl
|
||||||
- Proflies:
|
- Proflies:
|
||||||
+ profiles now depend on power-profile-daemon plus kernel patches for support of platform_profile
|
+ profiles now depend on power-profile-daemon plus kernel patches for support of platform_profile
|
||||||
- if your system supports fan-curves you will also require upcoming kernel patches for this
|
- if your system supports fan-curves you will also require upcoming kernel patches for this
|
||||||
|
|||||||
3
Cargo.lock
generated
3
Cargo.lock
generated
@@ -1069,9 +1069,8 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "supergfxctl"
|
name = "supergfxctl"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
|
source = "git+https://gitlab.com/asus-linux/supergfxctl.git?tag=2.0.0#3f040cd3ec334242631122cd038aa361cc860be6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"env_logger",
|
|
||||||
"gumdrop",
|
|
||||||
"log",
|
"log",
|
||||||
"logind-zbus",
|
"logind-zbus",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-supported", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "supergfx"]
|
members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-supported", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
|
|||||||
15
Makefile
15
Makefile
@@ -15,11 +15,7 @@ BIN_C := asusctl
|
|||||||
BIN_D := asusd
|
BIN_D := asusd
|
||||||
BIN_U := asusd-user
|
BIN_U := asusd-user
|
||||||
BIN_N := asus-notify
|
BIN_N := asus-notify
|
||||||
BIN_SD := supergfxd
|
|
||||||
BIN_SC := supergfxctl
|
|
||||||
LEDCFG := asusd-ledmodes.toml
|
LEDCFG := asusd-ledmodes.toml
|
||||||
X11CFG := 90-nvidia-screen-G05.conf
|
|
||||||
PMRULES := 90-asusd-nvidia-pm.rules
|
|
||||||
|
|
||||||
SRC := Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.rs')
|
SRC := Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.rs')
|
||||||
|
|
||||||
@@ -71,13 +67,6 @@ install:
|
|||||||
$(INSTALL_DATA) "./data/completions/asusctl.fish" "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish"
|
$(INSTALL_DATA) "./data/completions/asusctl.fish" "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish"
|
||||||
cd rog-anime/data && find "./anime" -type f -exec install -Dm 755 "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \;
|
cd rog-anime/data && find "./anime" -type f -exec install -Dm 755 "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \;
|
||||||
|
|
||||||
$(INSTALL_PROGRAM) "./target/release/$(BIN_SD)" "$(DESTDIR)$(bindir)/$(BIN_SD)"
|
|
||||||
$(INSTALL_PROGRAM) "./target/release/$(BIN_SC)" "$(DESTDIR)$(bindir)/$(BIN_SC)"
|
|
||||||
$(INSTALL_DATA) "./supergfx/data/$(BIN_SD).service" "$(DESTDIR)$(libdir)/systemd/system/$(BIN_SD).service"
|
|
||||||
$(INSTALL_DATA) "./supergfx/data/org.supergfxctl.Daemon.conf" "$(DESTDIR)$(datarootdir)/dbus-1/system.d/org.supergfxctl.Daemon.conf"
|
|
||||||
$(INSTALL_DATA) "./supergfx/data/$(X11CFG)" "$(DESTDIR)$(datarootdir)/X11/xorg.conf.d/$(X11CFG)"
|
|
||||||
$(INSTALL_DATA) "./supergfx/data/$(PMRULES)" "$(DESTDIR)$(libdir)/udev/rules.d/$(PMRULES)"
|
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
rm -f "$(DESTDIR)$(bindir)/$(BIN_C)"
|
rm -f "$(DESTDIR)$(bindir)/$(BIN_C)"
|
||||||
rm -f "$(DESTDIR)$(bindir)/$(BIN_D)"
|
rm -f "$(DESTDIR)$(bindir)/$(BIN_D)"
|
||||||
@@ -101,10 +90,6 @@ uninstall:
|
|||||||
rm -f "$(DESTDIR)$(zshcpl)/_asusctl"
|
rm -f "$(DESTDIR)$(zshcpl)/_asusctl"
|
||||||
rm -f "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish"
|
rm -f "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish"
|
||||||
rm -rf "$(DESTDIR)$(datarootdir)/asusd"
|
rm -rf "$(DESTDIR)$(datarootdir)/asusd"
|
||||||
rm -f "$(DESTDIR)$(bindir)/$(BIN_SC)"
|
|
||||||
rm -f "$(DESTDIR)$(bindir)/$(BIN_SD)"
|
|
||||||
rm -f "$(DESTDIR)$(libdir)/systemd/system/$(BIN_SD).service"
|
|
||||||
rm -f "$(DESTDIR)$(datarootdir)/dbus-1/system.d/org.supergfxctl.Daemon.conf"
|
|
||||||
|
|
||||||
update:
|
update:
|
||||||
cargo update
|
cargo update
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ rog_dbus = { path = "../rog-dbus" }
|
|||||||
rog_aura = { path = "../rog-aura" }
|
rog_aura = { path = "../rog-aura" }
|
||||||
rog_supported = { path = "../rog-supported" }
|
rog_supported = { path = "../rog-supported" }
|
||||||
rog_profiles = { path = "../rog-profiles" }
|
rog_profiles = { path = "../rog-profiles" }
|
||||||
supergfxctl = { path = "../supergfx" }
|
supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", tag = "2.0.0" }
|
||||||
|
|
||||||
[dependencies.notify-rust]
|
[dependencies.notify-rust]
|
||||||
version = "^4.3"
|
version = "^4.3"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ rog_profiles = { path = "../rog-profiles" }
|
|||||||
rog_supported = { path = "../rog-supported" }
|
rog_supported = { path = "../rog-supported" }
|
||||||
daemon = { path = "../daemon" }
|
daemon = { path = "../daemon" }
|
||||||
gumdrop = "^0.8"
|
gumdrop = "^0.8"
|
||||||
supergfxctl = { path = "../supergfx" }
|
supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", tag = "2.0.0" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tinybmp = "^0.2.3"
|
tinybmp = "^0.2.3"
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ rog_anime = { path = "../rog-anime" }
|
|||||||
rog_aura = { path = "../rog-aura" }
|
rog_aura = { path = "../rog-aura" }
|
||||||
rog_profiles = { path = "../rog-profiles" }
|
rog_profiles = { path = "../rog-profiles" }
|
||||||
rog_supported = { path = "../rog-supported" }
|
rog_supported = { path = "../rog-supported" }
|
||||||
supergfxctl = { path = "../supergfx" }
|
supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", tag = "2.0.0" }
|
||||||
zbus = "^1.9"
|
zbus = "^1.9"
|
||||||
zbus_macros = "^1.9"
|
zbus_macros = "^1.9"
|
||||||
zvariant = "^2.8"
|
zvariant = "^2.8"
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "supergfxctl"
|
|
||||||
version = "2.0.0"
|
|
||||||
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"]
|
|
||||||
default = ["daemon", "cli"]
|
|
||||||
|
|
||||||
[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"]
|
|
||||||
default-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"
|
|
||||||
|
|
||||||
sysfs-class = "^0.1.2"
|
|
||||||
|
|
||||||
env_logger = { version = "^0.8", optional = true }
|
|
||||||
gumdrop = { version = "^0.8", optional = true }
|
|
||||||
373
supergfx/LICENSE
373
supergfx/LICENSE
@@ -1,373 +0,0 @@
|
|||||||
Mozilla 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.
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
VERSION := $(shell grep -Pm1 'version = "(\d.\d.\d)"' daemon/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
|
|
||||||
X11CFG := 90-nvidia-screen-G05.conf
|
|
||||||
PMRULES := 90-asusd-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.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/$(BIN_SD).service" "$(DESTDIR)$(libdir)/systemd/system/$(BIN_SD).service"
|
|
||||||
$(INSTALL_DATA) "./data/org.supergfxctl.Daemon.conf" "$(DESTDIR)$(datarootdir)/dbus-1/system.d/org.supergfxctl.Daemon.conf"
|
|
||||||
$(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/$(BIN_SD).service"
|
|
||||||
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_asusctl_$(VERSION).tar.xz vendor
|
|
||||||
rm -rf vendor
|
|
||||||
|
|
||||||
build:
|
|
||||||
ifeq ($(VENDORED),1)
|
|
||||||
@echo "version = $(VERSION)"
|
|
||||||
tar pxf vendor_asusctl_$(VERSION).tar.xz
|
|
||||||
endif
|
|
||||||
cargo build $(ARGS)
|
|
||||||
|
|
||||||
.PHONY: all clean distclean install uninstall update build
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# 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"
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
Section "ServerLayout"
|
|
||||||
Identifier "layout"
|
|
||||||
Option "AllowNVIDIAGPUScreens"
|
|
||||||
EndSection
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<!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>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
[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.target
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
//! Basic CLI tool to control the `supergfxd` daemon
|
|
||||||
|
|
||||||
use std::{env::args, sync::mpsc::channel};
|
|
||||||
use supergfxctl::{
|
|
||||||
gfx_vendors::{GfxRequiredUserAction, GfxVendors},
|
|
||||||
special::{get_asus_gsync_gfx_mode, has_asus_gsync_gfx_mode},
|
|
||||||
zbus_proxy::GfxProxy,
|
|
||||||
};
|
|
||||||
|
|
||||||
use gumdrop::Options;
|
|
||||||
use zbus::Connection;
|
|
||||||
|
|
||||||
#[derive(Default, Options)]
|
|
||||||
struct CliStart {
|
|
||||||
#[options(help = "print help message")]
|
|
||||||
help: bool,
|
|
||||||
#[options(
|
|
||||||
meta = "",
|
|
||||||
help = "Set graphics mode: <nvidia, hybrid, compute, integrated>"
|
|
||||||
)]
|
|
||||||
mode: Option<GfxVendors>,
|
|
||||||
#[options(help = "Get the current mode")]
|
|
||||||
get: bool,
|
|
||||||
#[options(help = "Get the current power status")]
|
|
||||||
pow: bool,
|
|
||||||
#[options(help = "Do not ask for confirmation")]
|
|
||||||
force: 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)?;
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
eprintln!("source {}", err);
|
|
||||||
std::process::exit(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn do_gfx(command: CliStart) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
if command.mode.is_none() && !command.get && !command.pow && !command.force || 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 {
|
|
||||||
println!("You can not change modes until you turn dedicated/G-Sync off and reboot");
|
|
||||||
std::process::exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("If anything fails check `journalctl -b -u supergfxd`\n");
|
|
||||||
println!("Note that nvidia-drm.modeset=0 is required in kernel cmdline to enable the nvidia drivers to be unloaded on demand`\n");
|
|
||||||
|
|
||||||
proxy.gfx_write_mode(&mode).map_err(|err|{
|
|
||||||
println!("Graphics mode change error. You may be in an invalid state.");
|
|
||||||
println!("Check mode with `-g` and switch to opposite\nmode to correct it, e.g: if integrated, switch to hybrid, or if nvidia, switch to integrated.\n");
|
|
||||||
err
|
|
||||||
})?;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
proxy.next_signal()?;
|
|
||||||
|
|
||||||
if let Ok(res) = rx.try_recv() {
|
|
||||||
match res {
|
|
||||||
GfxRequiredUserAction::Integrated => {
|
|
||||||
println!(
|
|
||||||
"You must change to Integrated before you can change to {}",
|
|
||||||
<&str>::from(mode)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
GfxRequiredUserAction::Logout | GfxRequiredUserAction::Reboot => {
|
|
||||||
println!(
|
|
||||||
"Graphics mode changed to {}. User action required is: {}",
|
|
||||||
<&str>::from(mode),
|
|
||||||
<&str>::from(&res)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
GfxRequiredUserAction::None => {
|
|
||||||
println!("Graphics mode changed to {}", <&str>::from(mode));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::process::exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if command.get {
|
|
||||||
let res = proxy.gfx_get_mode()?;
|
|
||||||
println!("Current graphics mode: {}", <&str>::from(res));
|
|
||||||
}
|
|
||||||
if command.pow {
|
|
||||||
let res = proxy.gfx_get_pwr()?;
|
|
||||||
println!("Current power status: {}", <&str>::from(&res));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
use log::{error, warn};
|
|
||||||
use serde_derive::{Deserialize, Serialize};
|
|
||||||
use std::fs::{File, OpenOptions};
|
|
||||||
use std::io::{Read, Write};
|
|
||||||
|
|
||||||
use crate::gfx_vendors::GfxVendors;
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct GfxConfig {
|
|
||||||
#[serde(skip)]
|
|
||||||
config_path: String,
|
|
||||||
/// The current mode set, also applies on boot
|
|
||||||
pub gfx_mode: GfxVendors,
|
|
||||||
/// Only for informational purposes
|
|
||||||
#[serde(skip)]
|
|
||||||
pub gfx_tmp_mode: Option<GfxVendors>,
|
|
||||||
/// 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 GfxConfig {
|
|
||||||
fn new(config_path: String) -> Self {
|
|
||||||
Self {
|
|
||||||
config_path,
|
|
||||||
gfx_mode: GfxVendors::Hybrid,
|
|
||||||
gfx_tmp_mode: None,
|
|
||||||
gfx_managed: true,
|
|
||||||
gfx_vfio_enable: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `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 {
|
|
||||||
warn!("Could not deserialise {}", config_path);
|
|
||||||
panic!("Please remove {} then restart service", 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.gfx_tmp_mode = self.gfx_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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,706 +0,0 @@
|
|||||||
use ::zbus::Connection;
|
|
||||||
use log::{error, info, warn};
|
|
||||||
use logind_zbus::{
|
|
||||||
types::{SessionClass, SessionInfo, SessionState, SessionType},
|
|
||||||
ManagerProxy, SessionProxy,
|
|
||||||
};
|
|
||||||
use std::{io::Write, ops::Add, path::Path, time::Instant};
|
|
||||||
use std::{process::Command, thread::sleep, time::Duration};
|
|
||||||
use std::{str::FromStr, sync::mpsc};
|
|
||||||
use std::{sync::Arc, sync::Mutex};
|
|
||||||
use sysfs_class::RuntimePM;
|
|
||||||
use sysfs_class::{PciDevice, SysClass};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
error::GfxError,
|
|
||||||
special::{get_asus_gsync_gfx_mode, has_asus_gsync_gfx_mode},
|
|
||||||
system::{GraphicsDevice, PciBus},
|
|
||||||
*,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::config::GfxConfig;
|
|
||||||
use super::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors};
|
|
||||||
|
|
||||||
const THREAD_TIMEOUT_MSG: &str = "GFX: thread time exceeded 3 minutes, exiting";
|
|
||||||
const NVIDIA_RUNTIME_STATUS_PATH: &str = "/sys/bus/pci/devices/0000:01:00.0/power/runtime_status";
|
|
||||||
|
|
||||||
pub struct CtrlGraphics {
|
|
||||||
bus: PciBus,
|
|
||||||
_amd: Vec<GraphicsDevice>,
|
|
||||||
_intel: Vec<GraphicsDevice>,
|
|
||||||
nvidia: Vec<GraphicsDevice>,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
other: Vec<GraphicsDevice>,
|
|
||||||
config: Arc<Mutex<GfxConfig>>,
|
|
||||||
thread_kill: Arc<Mutex<Option<mpsc::Sender<bool>>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CtrlGraphics {
|
|
||||||
pub fn new(config: Arc<Mutex<GfxConfig>>) -> std::io::Result<CtrlGraphics> {
|
|
||||||
let bus = PciBus::new()?;
|
|
||||||
info!("GFX: Rescanning PCI bus");
|
|
||||||
bus.rescan()?;
|
|
||||||
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!("GFX: {}: Function for {}", func.id(), parent.id());
|
|
||||||
functions.push(func.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
functions
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut amd = Vec::new();
|
|
||||||
let mut intel = Vec::new();
|
|
||||||
let mut nvidia = Vec::new();
|
|
||||||
let mut other = Vec::new();
|
|
||||||
for dev in devs.iter() {
|
|
||||||
let c = dev.class().map_err(|err| {
|
|
||||||
error!(
|
|
||||||
"GFX: device error: {}, {}",
|
|
||||||
dev.path().to_string_lossy(),
|
|
||||||
err
|
|
||||||
);
|
|
||||||
err
|
|
||||||
})?;
|
|
||||||
if 0x03 == (c >> 16) & 0xFF {
|
|
||||||
match dev.vendor()? {
|
|
||||||
0x1002 => {
|
|
||||||
info!("GFX: {}: AMD graphics", dev.id());
|
|
||||||
amd.push(GraphicsDevice::new(dev.id().to_owned(), functions(dev)));
|
|
||||||
}
|
|
||||||
0x10DE => {
|
|
||||||
info!("GFX: {}: NVIDIA graphics", dev.id());
|
|
||||||
dev.set_runtime_pm(sysfs_class::RuntimePowerManagement::On)?;
|
|
||||||
nvidia.push(GraphicsDevice::new(dev.id().to_owned(), functions(dev)));
|
|
||||||
}
|
|
||||||
0x8086 => {
|
|
||||||
info!("GFX: {}: Intel graphics", dev.id());
|
|
||||||
intel.push(GraphicsDevice::new(dev.id().to_owned(), functions(dev)));
|
|
||||||
}
|
|
||||||
vendor => {
|
|
||||||
info!("GFX: {}: Other({:X}) graphics", dev.id(), vendor);
|
|
||||||
other.push(GraphicsDevice::new(dev.id().to_owned(), functions(dev)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(CtrlGraphics {
|
|
||||||
bus,
|
|
||||||
_amd: amd,
|
|
||||||
_intel: intel,
|
|
||||||
nvidia,
|
|
||||||
other,
|
|
||||||
config,
|
|
||||||
thread_kill: Arc::new(Mutex::new(None)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Force reinit of all state, including reset of device state
|
|
||||||
pub fn reload(&mut self) -> Result<(), GfxError> {
|
|
||||||
self.auto_power()?;
|
|
||||||
info!("GFX: Reloaded gfx mode: {:?}", self.get_gfx_mode()?);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bus(&self) -> PciBus {
|
|
||||||
self.bus.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn devices(&self) -> Vec<GraphicsDevice> {
|
|
||||||
self.nvidia.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Save the selected `Vendor` mode to config
|
|
||||||
fn save_gfx_mode(vendor: GfxVendors, config: Arc<Mutex<GfxConfig>>) {
|
|
||||||
if let Ok(mut config) = config.lock() {
|
|
||||||
config.gfx_mode = vendor;
|
|
||||||
config.write();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Associated method to get which vendor mode is set
|
|
||||||
pub(super) fn get_gfx_mode(&self) -> Result<GfxVendors, GfxError> {
|
|
||||||
if let Ok(config) = self.config.lock() {
|
|
||||||
if let Some(mode) = config.gfx_tmp_mode {
|
|
||||||
return Ok(mode);
|
|
||||||
}
|
|
||||||
return Ok(config.gfx_mode);
|
|
||||||
}
|
|
||||||
// TODO: Error here
|
|
||||||
Ok(GfxVendors::Hybrid)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn get_runtime_status() -> Result<GfxPower, GfxError> {
|
|
||||||
let path = Path::new(NVIDIA_RUNTIME_STATUS_PATH);
|
|
||||||
if path.exists() {
|
|
||||||
let buf = std::fs::read_to_string(path)
|
|
||||||
.map_err(|err| GfxError::Read(path.to_string_lossy().to_string(), err))?;
|
|
||||||
Ok(GfxPower::from_str(&buf)?)
|
|
||||||
} else {
|
|
||||||
Ok(GfxPower::Off)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Some systems have a fallback service to load nouveau if nvidia fails
|
|
||||||
fn toggle_fallback_service(vendor: GfxVendors) -> Result<(), GfxError> {
|
|
||||||
let action = if vendor == GfxVendors::Nvidia {
|
|
||||||
info!("GFX: Enabling nvidia-fallback.service");
|
|
||||||
"enable"
|
|
||||||
} else {
|
|
||||||
info!("GFX: Disabling nvidia-fallback.service");
|
|
||||||
"disable"
|
|
||||||
};
|
|
||||||
|
|
||||||
let status = Command::new("systemctl")
|
|
||||||
.arg(action)
|
|
||||||
.arg("nvidia-fallback.service")
|
|
||||||
.status()
|
|
||||||
.map_err(|err| GfxError::Command("systemctl".into(), err))?;
|
|
||||||
|
|
||||||
if !status.success() {
|
|
||||||
// Error is ignored in case this service is removed
|
|
||||||
warn!(
|
|
||||||
"systemctl: {} (ignore warning if service does not exist!)",
|
|
||||||
status
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write the appropriate xorg config for the chosen mode
|
|
||||||
fn write_xorg_conf(vendor: GfxVendors) -> Result<(), GfxError> {
|
|
||||||
let text = if vendor == GfxVendors::Nvidia {
|
|
||||||
[PRIMARY_GPU_BEGIN, PRIMARY_GPU_NVIDIA, PRIMARY_GPU_END].concat()
|
|
||||||
} else {
|
|
||||||
[PRIMARY_GPU_BEGIN, PRIMARY_GPU_END].concat()
|
|
||||||
};
|
|
||||||
|
|
||||||
if !Path::new(XORG_PATH).exists() {
|
|
||||||
std::fs::create_dir(XORG_PATH).map_err(|err| GfxError::Write(XORG_PATH.into(), err))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let file = XORG_PATH.to_string().add(XORG_FILE);
|
|
||||||
info!("GFX: Writing {}", file);
|
|
||||||
let mut file = std::fs::OpenOptions::new()
|
|
||||||
.create(true)
|
|
||||||
.truncate(true)
|
|
||||||
.write(true)
|
|
||||||
.open(&file)
|
|
||||||
.map_err(|err| GfxError::Write(file, err))?;
|
|
||||||
|
|
||||||
file.write_all(&text)
|
|
||||||
.and_then(|_| file.sync_all())
|
|
||||||
.map_err(|err| GfxError::Write(MODPROBE_PATH.into(), err))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates the full modprobe.conf required for vfio pass-through
|
|
||||||
fn get_vfio_conf(devices: &[GraphicsDevice]) -> Vec<u8> {
|
|
||||||
let mut vifo = MODPROBE_VFIO.to_vec();
|
|
||||||
for (d_count, dev) in devices.iter().enumerate() {
|
|
||||||
for (f_count, func) in dev.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 < dev.functions().len() - 1 {
|
|
||||||
vifo.append(&mut vec![b',']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if d_count < dev.functions().len() - 1 {
|
|
||||||
vifo.append(&mut vec![b',']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut conf = MODPROBE_INTEGRATED.to_vec();
|
|
||||||
conf.append(&mut vifo);
|
|
||||||
conf
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_modprobe_conf(vendor: GfxVendors, devices: &[GraphicsDevice]) -> Result<(), GfxError> {
|
|
||||||
info!("GFX: Writing {}", MODPROBE_PATH);
|
|
||||||
let content = match vendor {
|
|
||||||
GfxVendors::Nvidia | GfxVendors::Hybrid => {
|
|
||||||
let mut base = MODPROBE_BASE.to_vec();
|
|
||||||
base.append(&mut MODPROBE_DRM_MODESET.to_vec());
|
|
||||||
base
|
|
||||||
}
|
|
||||||
GfxVendors::Vfio => Self::get_vfio_conf(devices),
|
|
||||||
GfxVendors::Integrated => MODPROBE_INTEGRATED.to_vec(),
|
|
||||||
GfxVendors::Compute => MODPROBE_BASE.to_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(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unbind_remove_nvidia(devices: &[GraphicsDevice]) -> Result<(), GfxError> {
|
|
||||||
// Unbind NVIDIA graphics devices and their functions
|
|
||||||
let unbinds = devices.iter().map(|dev| dev.unbind());
|
|
||||||
// Remove NVIDIA graphics devices and their functions
|
|
||||||
let removes = devices.iter().map(|dev| dev.remove());
|
|
||||||
unbinds
|
|
||||||
.chain(removes)
|
|
||||||
.collect::<Result<_, _>>()
|
|
||||||
.map_err(|err| GfxError::Command("device unbind error".into(), err))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unbind_only(devices: &[GraphicsDevice]) -> Result<(), GfxError> {
|
|
||||||
let unbinds = devices.iter().map(|dev| dev.unbind());
|
|
||||||
unbinds
|
|
||||||
.collect::<Result<_, _>>()
|
|
||||||
.map_err(|err| GfxError::Command("device unbind error".into(), err))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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!("GFX: {}", 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!("GFX: 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 is_logout_required(&self, vendor: GfxVendors) -> GfxRequiredUserAction {
|
|
||||||
if let Ok(config) = self.config.lock() {
|
|
||||||
let current = config.gfx_mode;
|
|
||||||
// Modes that can switch without logout
|
|
||||||
if matches!(
|
|
||||||
current,
|
|
||||||
GfxVendors::Integrated | GfxVendors::Vfio | GfxVendors::Compute
|
|
||||||
) && matches!(
|
|
||||||
vendor,
|
|
||||||
GfxVendors::Integrated | GfxVendors::Vfio | GfxVendors::Compute
|
|
||||||
) {
|
|
||||||
return GfxRequiredUserAction::None;
|
|
||||||
}
|
|
||||||
// Modes that require a switch to integrated first
|
|
||||||
if matches!(current, GfxVendors::Nvidia | GfxVendors::Hybrid)
|
|
||||||
&& matches!(vendor, GfxVendors::Compute | GfxVendors::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(
|
|
||||||
vendor: GfxVendors,
|
|
||||||
vfio_enable: bool,
|
|
||||||
devices: &[GraphicsDevice],
|
|
||||||
bus: &PciBus,
|
|
||||||
) -> Result<(), GfxError> {
|
|
||||||
// Rescan before doing remove or add drivers
|
|
||||||
bus.rescan()?;
|
|
||||||
// Make sure the power management is set to auto for nvidia devices
|
|
||||||
let devs = PciDevice::all()?;
|
|
||||||
for dev in devs.iter() {
|
|
||||||
let c = dev.class().map_err(|err| {
|
|
||||||
error!(
|
|
||||||
"GFX: device error: {}, {}",
|
|
||||||
dev.path().to_string_lossy(),
|
|
||||||
err
|
|
||||||
);
|
|
||||||
err
|
|
||||||
})?;
|
|
||||||
if 0x03 == (c >> 16) & 0xFF && dev.vendor()? == 0x10DE {
|
|
||||||
info!("GFX: {}: NVIDIA graphics, setting PM to auto", dev.id());
|
|
||||||
dev.set_runtime_pm(sysfs_class::RuntimePowerManagement::On)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Only these modes should have xorg config
|
|
||||||
if matches!(
|
|
||||||
vendor,
|
|
||||||
GfxVendors::Nvidia | GfxVendors::Hybrid | GfxVendors::Integrated
|
|
||||||
) {
|
|
||||||
Self::write_xorg_conf(vendor)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write different modprobe to enable boot control to work
|
|
||||||
Self::write_modprobe_conf(vendor, devices)?;
|
|
||||||
|
|
||||||
match vendor {
|
|
||||||
GfxVendors::Nvidia | GfxVendors::Hybrid | GfxVendors::Compute => {
|
|
||||||
if vfio_enable {
|
|
||||||
for driver in VFIO_DRIVERS.iter() {
|
|
||||||
Self::do_driver_action(driver, "rmmod")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for driver in NVIDIA_DRIVERS.iter() {
|
|
||||||
Self::do_driver_action(driver, "modprobe")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GfxVendors::Vfio => {
|
|
||||||
if vfio_enable {
|
|
||||||
Self::do_driver_action("nouveau", "rmmod")?;
|
|
||||||
for driver in NVIDIA_DRIVERS.iter() {
|
|
||||||
Self::do_driver_action(driver, "rmmod")?;
|
|
||||||
}
|
|
||||||
Self::unbind_only(devices)?;
|
|
||||||
Self::do_driver_action("vfio-pci", "modprobe")?;
|
|
||||||
} else {
|
|
||||||
return Err(GfxError::VfioDisabled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GfxVendors::Integrated => {
|
|
||||||
Self::do_driver_action("nouveau", "rmmod")?;
|
|
||||||
if vfio_enable {
|
|
||||||
for driver in VFIO_DRIVERS.iter() {
|
|
||||||
Self::do_driver_action(driver, "rmmod")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for driver in NVIDIA_DRIVERS.iter() {
|
|
||||||
Self::do_driver_action(driver, "rmmod")?;
|
|
||||||
}
|
|
||||||
Self::unbind_remove_nvidia(devices)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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 create_mode_change_thread(
|
|
||||||
vendor: GfxVendors,
|
|
||||||
devices: Vec<GraphicsDevice>,
|
|
||||||
bus: PciBus,
|
|
||||||
thread_stop: mpsc::Receiver<bool>,
|
|
||||||
config: Arc<Mutex<GfxConfig>>,
|
|
||||||
) -> Result<String, GfxError> {
|
|
||||||
info!("GFX: display-manager thread started");
|
|
||||||
|
|
||||||
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 {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(stop) = thread_stop.try_recv() {
|
|
||||||
if stop {
|
|
||||||
return Ok("Graphics mode change was cancelled".into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// exit if 3 minutes pass
|
|
||||||
if Instant::now().duration_since(start_time).as_secs() > 180 {
|
|
||||||
warn!("{}", THREAD_TIMEOUT_MSG);
|
|
||||||
return Err(GfxError::DisplayManagerTimeout(THREAD_TIMEOUT_MSG.into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't spin at max speed
|
|
||||||
sleep(SLEEP_PERIOD);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = vendor;
|
|
||||||
// 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.gfx_tmp_mode = None;
|
|
||||||
//
|
|
||||||
let vfio_enable = config.gfx_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!(vendor, GfxVendors::Compute | GfxVendors::Vfio)
|
|
||||||
&& matches!(config.gfx_mode, GfxVendors::Nvidia | GfxVendors::Hybrid)
|
|
||||||
{
|
|
||||||
Self::do_mode_setup_tasks(GfxVendors::Integrated, vfio_enable, &devices, &bus)?;
|
|
||||||
Self::do_display_manager_action("restart")?;
|
|
||||||
mode_to_save = GfxVendors::Integrated;
|
|
||||||
} else {
|
|
||||||
Self::do_mode_setup_tasks(vendor, vfio_enable, &devices, &bus)?;
|
|
||||||
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 = vendor.into();
|
|
||||||
info!("GFX thread: Graphics mode changed to {} successfully", v);
|
|
||||||
Ok(format!("Graphics mode changed to {} successfully", v))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Before starting a new thread the old one *must* be cancelled
|
|
||||||
fn cancel_mode_change_thread(&self) {
|
|
||||||
if let Ok(lock) = self.thread_kill.lock() {
|
|
||||||
if let Some(tx) = lock.as_ref() {
|
|
||||||
// Cancel the running thread
|
|
||||||
info!("GFX: Cancelling previous thread");
|
|
||||||
tx.send(true)
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!("GFX thread: {}", err);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The thread is used only in cases where a logout is required
|
|
||||||
fn setup_mode_change_thread(&mut self, vendor: GfxVendors) {
|
|
||||||
let config = self.config.clone();
|
|
||||||
let devices = self.nvidia.clone();
|
|
||||||
let bus = self.bus.clone();
|
|
||||||
let (tx, rx) = mpsc::channel();
|
|
||||||
if let Ok(mut lock) = self.thread_kill.lock() {
|
|
||||||
*lock = Some(tx);
|
|
||||||
}
|
|
||||||
let thread_kill = self.thread_kill.clone();
|
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
Self::create_mode_change_thread(vendor, devices, bus, rx, config)
|
|
||||||
.map_err(|err| {
|
|
||||||
error!("GFX: {}", err);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
// clear the tx/rx when done
|
|
||||||
if let Ok(mut lock) = thread_kill.try_lock() {
|
|
||||||
*lock = 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, vendor: GfxVendors) -> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let vfio_enable = if let Ok(config) = self.config.try_lock() {
|
|
||||||
config.gfx_vfio_enable
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
if !vfio_enable && matches!(vendor, GfxVendors::Vfio) {
|
|
||||||
return Err(GfxError::VfioDisabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must always cancel any thread running
|
|
||||||
self.cancel_mode_change_thread();
|
|
||||||
// determine which method we need here
|
|
||||||
let action_required = self.is_logout_required(vendor);
|
|
||||||
|
|
||||||
match action_required {
|
|
||||||
GfxRequiredUserAction::Logout => {
|
|
||||||
info!("GFX: mode change requires a logout to complete");
|
|
||||||
self.setup_mode_change_thread(vendor);
|
|
||||||
}
|
|
||||||
GfxRequiredUserAction::Reboot => {
|
|
||||||
info!("GFX: mode change requires reboot");
|
|
||||||
let devices = self.nvidia.clone();
|
|
||||||
let bus = self.bus.clone();
|
|
||||||
Self::do_mode_setup_tasks(vendor, vfio_enable, &devices, &bus)?;
|
|
||||||
info!("GFX: Graphics mode changed to {}", <&str>::from(vendor));
|
|
||||||
}
|
|
||||||
GfxRequiredUserAction::Integrated => {
|
|
||||||
info!("GFX: mode change requires user to be in Integrated mode first");
|
|
||||||
}
|
|
||||||
GfxRequiredUserAction::None => {
|
|
||||||
info!("GFX: mode change does not require logout");
|
|
||||||
let devices = self.nvidia.clone();
|
|
||||||
let bus = self.bus.clone();
|
|
||||||
Self::do_mode_setup_tasks(vendor, vfio_enable, &devices, &bus)?;
|
|
||||||
info!("GFX: Graphics mode changed to {}", <&str>::from(vendor));
|
|
||||||
if let Ok(mut config) = self.config.try_lock() {
|
|
||||||
config.gfx_tmp_mode = None;
|
|
||||||
if matches!(vendor, GfxVendors::Vfio | GfxVendors::Compute) {
|
|
||||||
config.gfx_tmp_mode = Some(vendor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(action_required)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Used only on boot to set correct mode
|
|
||||||
fn auto_power(&mut self) -> Result<(), GfxError> {
|
|
||||||
let vendor = self.get_gfx_mode()?;
|
|
||||||
let devices = self.nvidia.clone();
|
|
||||||
let bus = self.bus.clone();
|
|
||||||
|
|
||||||
let vfio_enable = if let Ok(config) = self.config.try_lock() {
|
|
||||||
config.gfx_vfio_enable
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
Self::do_mode_setup_tasks(vendor, vfio_enable, &devices, &bus)?;
|
|
||||||
Self::toggle_fallback_service(vendor)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
use 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::GfxVendors,
|
|
||||||
special::{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 enable_gfx_switching = config.gfx_managed;
|
|
||||||
let config = Arc::new(Mutex::new(config));
|
|
||||||
|
|
||||||
// Graphics switching requires some checks on boot specifically for g-sync capable laptops
|
|
||||||
if enable_gfx_switching {
|
|
||||||
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)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
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.devices();
|
|
||||||
let bus = ctrl.bus();
|
|
||||||
CtrlGraphics::do_mode_setup_tasks(GfxVendors::Nvidia, false, &devices, &bus)?;
|
|
||||||
} else if ded == 0 {
|
|
||||||
info!("Dedicated GFX toggle is off");
|
|
||||||
let devices = ctrl.devices();
|
|
||||||
let bus = ctrl.bus();
|
|
||||||
CtrlGraphics::do_mode_setup_tasks(config.gfx_mode, false, &devices, &bus)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
use std::fmt;
|
|
||||||
use std::{error, 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),
|
|
||||||
Io(std::io::Error),
|
|
||||||
Zbus(zbus::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::Io(detail) => write!(f, "std::io error: {}", detail),
|
|
||||||
GfxError::Zbus(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<std::io::Error> for GfxError {
|
|
||||||
fn from(err: std::io::Error) -> Self {
|
|
||||||
GfxError::Io(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
use 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 GfxVendors {
|
|
||||||
Nvidia,
|
|
||||||
Integrated,
|
|
||||||
Compute,
|
|
||||||
Vfio,
|
|
||||||
Hybrid,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for GfxVendors {
|
|
||||||
type Err = GfxError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, GfxError> {
|
|
||||||
match s.to_lowercase().as_str() {
|
|
||||||
"nvidia" => Ok(GfxVendors::Nvidia),
|
|
||||||
"hybrid" => Ok(GfxVendors::Hybrid),
|
|
||||||
"compute" => Ok(GfxVendors::Compute),
|
|
||||||
"vfio" => Ok(GfxVendors::Vfio),
|
|
||||||
"integrated" => Ok(GfxVendors::Integrated),
|
|
||||||
"nvidia\n" => Ok(GfxVendors::Nvidia),
|
|
||||||
"hybrid\n" => Ok(GfxVendors::Hybrid),
|
|
||||||
"compute\n" => Ok(GfxVendors::Compute),
|
|
||||||
"vfio\n" => Ok(GfxVendors::Vfio),
|
|
||||||
"integrated\n" => Ok(GfxVendors::Integrated),
|
|
||||||
_ => Err(GfxError::ParseVendor),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&GfxVendors> for &str {
|
|
||||||
fn from(gfx: &GfxVendors) -> &'static str {
|
|
||||||
match gfx {
|
|
||||||
GfxVendors::Nvidia => "nvidia",
|
|
||||||
GfxVendors::Hybrid => "hybrid",
|
|
||||||
GfxVendors::Compute => "compute",
|
|
||||||
GfxVendors::Vfio => "vfio",
|
|
||||||
GfxVendors::Integrated => "integrated",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<GfxVendors> for &str {
|
|
||||||
fn from(gfx: GfxVendors) -> &'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 => "no action",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
/// The configuration for graphics. This should be saved and loaded on boot.
|
|
||||||
pub mod config;
|
|
||||||
/// 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;
|
|
||||||
/// System interface helpers.
|
|
||||||
pub mod system;
|
|
||||||
/// Defined DBUS Interface for supergfxctl
|
|
||||||
pub mod zbus_iface;
|
|
||||||
/// Defined DBUS Proxy for supergfxctl
|
|
||||||
pub mod zbus_proxy;
|
|
||||||
|
|
||||||
/// 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";
|
|
||||||
|
|
||||||
const NVIDIA_DRIVERS: [&str; 4] = ["nvidia_drm", "nvidia_modeset", "nvidia_uvm", "nvidia"];
|
|
||||||
|
|
||||||
const VFIO_DRIVERS: [&str; 5] = [
|
|
||||||
"vfio-pci",
|
|
||||||
"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_BASE: &[u8] = br#"# Automatically generated by supergfxd
|
|
||||||
blacklist nouveau
|
|
||||||
alias nouveau off
|
|
||||||
options nvidia NVreg_DynamicPowerManagement=0x02
|
|
||||||
"#;
|
|
||||||
|
|
||||||
static MODPROBE_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_BEGIN: &[u8] = br#"# Automatically generated by supergfxd
|
|
||||||
Section "OutputClass"
|
|
||||||
Identifier "nvidia"
|
|
||||||
MatchDriver "nvidia-drm"
|
|
||||||
Driver "nvidia"
|
|
||||||
Option "AllowEmptyInitialConfiguration" "true""#;
|
|
||||||
|
|
||||||
static PRIMARY_GPU_NVIDIA: &[u8] = br#"
|
|
||||||
Option "PrimaryGPU" "true""#;
|
|
||||||
|
|
||||||
static PRIMARY_GPU_END: &[u8] = br#"
|
|
||||||
EndSection"#;
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
use std::{fs::OpenOptions, io::Read, path::Path};
|
|
||||||
|
|
||||||
use crate::error::GfxError;
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
use log::{error, info, warn};
|
|
||||||
use std::fs::read_to_string;
|
|
||||||
use std::{fs::write, io, path::PathBuf};
|
|
||||||
use sysfs_class::{PciDevice, SysClass};
|
|
||||||
|
|
||||||
pub struct Module {
|
|
||||||
pub name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Module {
|
|
||||||
fn parse(line: &str) -> io::Result<Module> {
|
|
||||||
let mut parts = line.split(' ');
|
|
||||||
|
|
||||||
let name = parts
|
|
||||||
.next()
|
|
||||||
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "module name not found"))?;
|
|
||||||
|
|
||||||
Ok(Module {
|
|
||||||
name: name.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn all() -> io::Result<Vec<Module>> {
|
|
||||||
let mut modules = Vec::new();
|
|
||||||
|
|
||||||
let data = read_to_string("/proc/modules")?;
|
|
||||||
for line in data.lines() {
|
|
||||||
let module = Module::parse(line)?;
|
|
||||||
modules.push(module);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(modules)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct PciBus {
|
|
||||||
path: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PciBus {
|
|
||||||
pub fn new() -> io::Result<PciBus> {
|
|
||||||
let path = PathBuf::from("/sys/bus/pci");
|
|
||||||
if path.is_dir() {
|
|
||||||
Ok(PciBus { path })
|
|
||||||
} else {
|
|
||||||
Err(io::Error::new(
|
|
||||||
io::ErrorKind::NotFound,
|
|
||||||
"pci directory not found",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Will rescan the device tree, which adds all removed devices back
|
|
||||||
pub fn rescan(&self) -> io::Result<()> {
|
|
||||||
write(self.path.join("rescan"), "1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct GraphicsDevice {
|
|
||||||
_id: String,
|
|
||||||
functions: Vec<PciDevice>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GraphicsDevice {
|
|
||||||
pub fn new(id: String, functions: Vec<PciDevice>) -> GraphicsDevice {
|
|
||||||
GraphicsDevice { _id: id, functions }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exists(&self) -> bool {
|
|
||||||
self.functions.iter().any(|func| func.path().exists())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn functions(&self) -> &[PciDevice] {
|
|
||||||
&self.functions
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unbind(&self) -> Result<(), std::io::Error> {
|
|
||||||
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() {
|
|
||||||
io::ErrorKind::NotFound => (),
|
|
||||||
_ => {
|
|
||||||
error!("gfx driver: {:?}, {}", func.path(), err);
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rebind(&self) -> Result<(), std::io::Error> {
|
|
||||||
for func in self.functions.iter() {
|
|
||||||
if func.path().exists() {
|
|
||||||
match func.driver() {
|
|
||||||
Ok(driver) => {
|
|
||||||
info!("{}: Binding {}", driver.id(), func.id());
|
|
||||||
unsafe {
|
|
||||||
driver.bind(func).map_err(|err| {
|
|
||||||
error!("gfx bind: {}", err);
|
|
||||||
err
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => match err.kind() {
|
|
||||||
io::ErrorKind::NotFound => (),
|
|
||||||
_ => {
|
|
||||||
error!("gfx driver: {:?}, {}", func.path(), err);
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove(&self) -> Result<(), std::io::Error> {
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
use ::zbus::dbus_interface;
|
|
||||||
use log::{error, info, warn};
|
|
||||||
use zvariant::ObjectPath;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors},
|
|
||||||
DBUS_IFACE_PATH,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::controller::CtrlGraphics;
|
|
||||||
|
|
||||||
#[dbus_interface(name = "org.supergfxctl.Daemon")]
|
|
||||||
impl CtrlGraphics {
|
|
||||||
fn vendor(&self) -> zbus::fdo::Result<GfxVendors> {
|
|
||||||
self.get_gfx_mode().map_err(|err| {
|
|
||||||
error!("GFX: {}", err);
|
|
||||||
zbus::fdo::Error::Failed(format!("GFX fail: {}", err))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn power(&self) -> zbus::fdo::Result<GfxPower> {
|
|
||||||
Self::get_runtime_status().map_err(|err| {
|
|
||||||
error!("GFX: {}", err);
|
|
||||||
zbus::fdo::Error::Failed(format!("GFX fail: {}", err))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_vendor(&mut self, vendor: GfxVendors) -> zbus::fdo::Result<GfxRequiredUserAction> {
|
|
||||||
info!("GFX: Switching gfx mode to {}", <&str>::from(vendor));
|
|
||||||
let msg = self.set_gfx_mode(vendor).map_err(|err| {
|
|
||||||
error!("GFX: {}", err);
|
|
||||||
zbus::fdo::Error::Failed(format!("GFX fail: {}", err))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
self.notify_action(&msg)
|
|
||||||
.unwrap_or_else(|err| warn!("GFX: {}", err));
|
|
||||||
|
|
||||||
self.notify_gfx(&vendor)
|
|
||||||
.unwrap_or_else(|err| warn!("GFX: {}", err));
|
|
||||||
|
|
||||||
Ok(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[dbus_interface(signal)]
|
|
||||||
fn notify_gfx(&self, vendor: &GfxVendors) -> zbus::Result<()> {}
|
|
||||||
|
|
||||||
#[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!("GFX: CtrlGraphics: add_to_server {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
//! # 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::{GfxPower, GfxRequiredUserAction, GfxVendors},
|
|
||||||
DBUS_IFACE_PATH,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[dbus_proxy(interface = "org.supergfxctl.Daemon")]
|
|
||||||
trait Daemon {
|
|
||||||
/// Power method
|
|
||||||
fn power(&self) -> zbus::Result<GfxPower>;
|
|
||||||
|
|
||||||
/// SetVendor method
|
|
||||||
fn set_vendor(&self, vendor: &GfxVendors) -> zbus::Result<GfxRequiredUserAction>;
|
|
||||||
|
|
||||||
/// Vendor method
|
|
||||||
fn vendor(&self) -> zbus::Result<GfxVendors>;
|
|
||||||
|
|
||||||
/// NotifyAction signal
|
|
||||||
#[dbus_proxy(signal)]
|
|
||||||
fn notify_action(&self, action: GfxRequiredUserAction) -> zbus::Result<()>;
|
|
||||||
|
|
||||||
/// NotifyGfx signal
|
|
||||||
#[dbus_proxy(signal)]
|
|
||||||
fn notify_gfx(&self, vendor: GfxVendors) -> 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 gfx_get_pwr(&self) -> Result<GfxPower> {
|
|
||||||
self.0.power()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn gfx_get_mode(&self) -> Result<GfxVendors> {
|
|
||||||
self.0.vendor()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn gfx_write_mode(&self, vendor: &GfxVendors) -> Result<GfxRequiredUserAction> {
|
|
||||||
self.0.set_vendor(vendor)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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<GfxVendors>) -> 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user