Upload files to "/"
This commit is contained in:
commit
e0f579e740
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Boyd Gordon
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,52 @@
|
||||||
|
# Maintainer: G34RZ <g3arz@dockservices.co>
|
||||||
|
pkgbase=system76-power-gui-x11
|
||||||
|
pkgname=system76-power-gui-x11
|
||||||
|
pkgver=0.2.1
|
||||||
|
pkgrel=0
|
||||||
|
pkgdesc="GUI application for System76 Power Management"
|
||||||
|
arch=('x86_64')
|
||||||
|
url="https://gitea.dockservices.co/G34RZ/system76-power-GUI-x11.git"
|
||||||
|
license=('MIT')
|
||||||
|
depends=('system76-power' 'polkit' 'gtk3')
|
||||||
|
makedepends=('go' 'gcc' 'git')
|
||||||
|
|
||||||
|
prepare() {
|
||||||
|
mkdir -p "$srcdir/$pkgname-$pkgver"
|
||||||
|
cd "$startdir"
|
||||||
|
cp -t "$srcdir/$pkgname-$pkgver/" \
|
||||||
|
power-gui.go \
|
||||||
|
system76-power-gui-x11.desktop \
|
||||||
|
90-system76-power-gui-x11.rules \
|
||||||
|
LICENSE \
|
||||||
|
go.mod \
|
||||||
|
go.sum \
|
||||||
|
2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
cd "$srcdir/$pkgname-$pkgver"
|
||||||
|
export MAKEFLAGS="-j$(nproc)"
|
||||||
|
export CGO_CPPFLAGS="${CPPFLAGS}"
|
||||||
|
export CGO_CFLAGS="${CFLAGS}"
|
||||||
|
export CGO_CXXFLAGS="${CXXFLAGS}"
|
||||||
|
export CGO_LDFLAGS="${LDFLAGS}"
|
||||||
|
export GOFLAGS="-buildmode=pie -trimpath -mod=readonly -modcacherw"
|
||||||
|
export GOMAXPROCS=$(nproc)
|
||||||
|
go build -o system76-power-gui-x11
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
cd "$srcdir/$pkgname-$pkgver"
|
||||||
|
|
||||||
|
# Install binary
|
||||||
|
install -Dm755 system76-power-gui-x11 "$pkgdir/usr/bin/system76-power-gui-x11"
|
||||||
|
|
||||||
|
# Install X11 desktop entry
|
||||||
|
install -Dm644 system76-power-gui-x11.desktop "$pkgdir/usr/share/applications/system76-power-gui-x11.desktop"
|
||||||
|
|
||||||
|
# Install polkit rules
|
||||||
|
install -Dm644 90-system76-power-gui-x11.rules "$pkgdir/usr/share/polkit-1/rules.d/90-system76-power-gui-x11.rules"
|
||||||
|
|
||||||
|
# Install license
|
||||||
|
install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
# System76 Power GUI
|
||||||
|
|
||||||
|
A simple graphical user interface for System76 Power Management.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- View current power profile
|
||||||
|
- Switch between Battery, Balanced, and Turbo modes
|
||||||
|
- Automatic profile persistence
|
||||||
|
- System tray integration
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- system76-power
|
||||||
|
- polkit (for privilege escalation)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Arch Linux
|
||||||
|
```bash
|
||||||
|
# Build and install the package
|
||||||
|
makepkg -si
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
Launch the application from your desktop environment's application menu or run:
|
||||||
|
```bash
|
||||||
|
system76-power-gui
|
||||||
|
```
|
|
@ -0,0 +1,230 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"fyne.io/fyne/v2"
|
||||||
|
"fyne.io/fyne/v2/app"
|
||||||
|
"fyne.io/fyne/v2/container"
|
||||||
|
"fyne.io/fyne/v2/dialog"
|
||||||
|
"fyne.io/fyne/v2/widget"
|
||||||
|
)
|
||||||
|
|
||||||
|
// getDisplayServerType returns the type of display server being used
|
||||||
|
func getDisplayServerType() string {
|
||||||
|
return "X11"
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDesktopEntry returns the appropriate desktop entry path
|
||||||
|
func getDesktopEntry() string {
|
||||||
|
return "/usr/share/applications/system76-power-gui-x11.desktop"
|
||||||
|
}
|
||||||
|
|
||||||
|
// executeCommand runs a given command with sudo and returns its output or an error.
|
||||||
|
func executeCommand(command string, args ...string) (string, error) {
|
||||||
|
cmdArgs := append([]string{command}, args...)
|
||||||
|
cmd := exec.Command("pkexec", cmdArgs...)
|
||||||
|
|
||||||
|
// Set up command environment
|
||||||
|
cmd.Env = append(os.Environ(),
|
||||||
|
"DISPLAY="+os.Getenv("DISPLAY"),
|
||||||
|
)
|
||||||
|
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("command execution failed: %v\nOutput: %s", err, string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(output), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getStatus retrieves the current system76-power profile.
|
||||||
|
func getStatus() (string, error) {
|
||||||
|
// First check if system76-power is available
|
||||||
|
if _, err := exec.LookPath("system76-power"); err != nil {
|
||||||
|
return "", fmt.Errorf("system76-power is not installed or not in PATH: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get the profile
|
||||||
|
output, err := executeCommand("system76-power", "profile")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to get current profile: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
profile := strings.TrimSpace(output)
|
||||||
|
if profile == "" {
|
||||||
|
return "", fmt.Errorf("received empty profile from system76-power")
|
||||||
|
}
|
||||||
|
|
||||||
|
return profile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isNonCriticalError checks if the error is non-critical (like SCSI errors)
|
||||||
|
func isNonCriticalError(output string) bool {
|
||||||
|
// Check for SCSI-related errors in DBus error output
|
||||||
|
return strings.Contains(output, "failed to set scsi host profiles") &&
|
||||||
|
strings.Contains(output, "org.freedesktop.DBus.Error.Failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// trySetProfile attempts to set the profile once
|
||||||
|
func trySetProfile(profile string) error {
|
||||||
|
output, err := executeCommand("system76-power", "profile", profile)
|
||||||
|
if err != nil {
|
||||||
|
// Check if this is a non-critical error
|
||||||
|
if isNonCriticalError(output) {
|
||||||
|
// Wait a moment for the profile to be applied
|
||||||
|
time.Sleep(time.Millisecond * 500)
|
||||||
|
|
||||||
|
// Verify if the profile was set despite the SCSI error
|
||||||
|
currentProfile, checkErr := getStatus()
|
||||||
|
if checkErr == nil && currentProfile == profile {
|
||||||
|
fmt.Printf("Profile set to %s (with non-critical SCSI warnings)\n", profile)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("failed to set profile: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setProfile sets the system76-power profile to the specified mode with one retry.
|
||||||
|
func setProfile(profile string) error {
|
||||||
|
// First attempt
|
||||||
|
err := trySetProfile(profile)
|
||||||
|
if err == nil {
|
||||||
|
fmt.Printf("Profile set to %s successfully.\n", profile)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If first attempt failed, wait a bit and retry once
|
||||||
|
fmt.Printf("First attempt failed, retrying after delay...\n")
|
||||||
|
time.Sleep(time.Second * 1)
|
||||||
|
|
||||||
|
// Second attempt
|
||||||
|
err = trySetProfile(profile)
|
||||||
|
if err == nil {
|
||||||
|
fmt.Printf("Profile set to %s successfully on second attempt.\n", profile)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("failed to set profile to %s after retry: %v", profile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
type powerApp struct {
|
||||||
|
app fyne.App
|
||||||
|
window fyne.Window
|
||||||
|
}
|
||||||
|
|
||||||
|
// createTrayMenu creates the menu for the window
|
||||||
|
func (p *powerApp) createMenu() *fyne.MainMenu {
|
||||||
|
menu := fyne.NewMainMenu(
|
||||||
|
fyne.NewMenu("Power Profile",
|
||||||
|
fyne.NewMenuItem("Battery Mode", func() {
|
||||||
|
if err := setProfile("battery"); err != nil {
|
||||||
|
showError(err, p.window)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
fyne.NewMenuItem("Balanced Mode", func() {
|
||||||
|
if err := setProfile("balanced"); err != nil {
|
||||||
|
showError(err, p.window)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
fyne.NewMenuItem("Performance Mode", func() {
|
||||||
|
if err := setProfile("performance"); err != nil {
|
||||||
|
showError(err, p.window)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
fyne.NewMenuItemSeparator(),
|
||||||
|
fyne.NewMenuItem("Quit", func() { p.app.Quit() }),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return menu
|
||||||
|
}
|
||||||
|
|
||||||
|
// showError displays an error in a dialog with selectable text
|
||||||
|
func showError(err error, window fyne.Window) {
|
||||||
|
errEntry := widget.NewMultiLineEntry()
|
||||||
|
errEntry.SetText(err.Error())
|
||||||
|
errEntry.Disable()
|
||||||
|
d := dialog.NewCustom("Error", "OK", errEntry, window)
|
||||||
|
d.Resize(fyne.NewSize(400, 100))
|
||||||
|
d.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateStatus periodically updates the status label
|
||||||
|
func (p *powerApp) updateStatus(statusLabel *widget.Label) {
|
||||||
|
for {
|
||||||
|
status, err := getStatus()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error updating status: %v\n", err)
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
statusLabel.SetText(fmt.Sprintf("%s", status))
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Create application
|
||||||
|
a := app.New()
|
||||||
|
w := a.NewWindow("System76 Power Profile")
|
||||||
|
|
||||||
|
// Create our app structure
|
||||||
|
powerApp := &powerApp{
|
||||||
|
app: a,
|
||||||
|
window: w,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up menu
|
||||||
|
w.SetMainMenu(powerApp.createMenu())
|
||||||
|
|
||||||
|
// Create main window content
|
||||||
|
statusLabel := widget.NewLabel("Current profile: Unknown")
|
||||||
|
|
||||||
|
// Start background status updater
|
||||||
|
go powerApp.updateStatus(statusLabel)
|
||||||
|
|
||||||
|
// Create profile buttons
|
||||||
|
batteryButton := widget.NewButton("Battery Mode", func() {
|
||||||
|
if err := setProfile("battery"); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error setting profile: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
balancedButton := widget.NewButton("Balanced Mode", func() {
|
||||||
|
if err := setProfile("balanced"); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error setting profile: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
performanceButton := widget.NewButton("Performance Mode", func() {
|
||||||
|
if err := setProfile("performance"); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error setting profile: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Set up window content
|
||||||
|
w.SetContent(container.NewVBox(
|
||||||
|
statusLabel,
|
||||||
|
widget.NewSeparator(),
|
||||||
|
batteryButton,
|
||||||
|
balancedButton,
|
||||||
|
performanceButton,
|
||||||
|
))
|
||||||
|
|
||||||
|
// Set window properties
|
||||||
|
w.Resize(fyne.NewSize(300, 200))
|
||||||
|
w.SetCloseIntercept(func() { w.Hide() }) // Hide instead of close
|
||||||
|
|
||||||
|
// Start application
|
||||||
|
w.Show()
|
||||||
|
a.Run()
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
[Desktop Entry]
|
||||||
|
Name=System76 Power GUI
|
||||||
|
Comment=GUI for System76-Power Management
|
||||||
|
Exec=system76-power-gui-x11
|
||||||
|
Icon=preferences-system-power
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
Categories=Settings;HardwareSettings;
|
||||||
|
Keywords=system76;power;profile;x11;
|
||||||
|
NoDisplay=false
|
||||||
|
Hidden=false
|
||||||
|
X-DBUS-ServiceName=org.system76.PowerGUI
|
||||||
|
X-DBUS-StartupType=none
|
||||||
|
X-GNOME-Bugzilla-Bugzilla=GNOME
|
||||||
|
X-GNOME-Bugzilla-Product=System76-Power-GUI
|
||||||
|
X-GNOME-Bugzilla-Component=General
|
||||||
|
X-GNOME-Bugzilla-Version=1.0.0
|
||||||
|
X-GNOME-Autostart-enabled=true
|
||||||
|
Name[en_US]=System76 Power GUI X11
|
||||||
|
Comment[en_US]=GUI for System76-Power Management
|
||||||
|
X-Ubuntu-Gettext-Domain=system76-power-gui
|
||||||
|
X-SessionType=x11
|
Loading…
Reference in New Issue