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