Skip to content

Commit

Permalink
kbdlight_timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
GrigorenkoPV authored and nekr0z committed May 5, 2023
1 parent 063326c commit 31b7615
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 15 deletions.
18 changes: 18 additions & 0 deletions applet.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ var (
func onReady() {
logTrace.Println(localizer.MustLocalize(&i18n.LocalizeConfig{DefaultMessage: &i18n.Message{ID: "PreparingTray", Other: "Setting up menu..."}}))
systray.SetIcon(getIcon(iconPath, defaultIcon))
mKbdlightTimeout := systray.AddMenuItem("", "")
systray.AddSeparator()
mStatus := systray.AddMenuItem("", "")
systray.AddSeparator()
mOff := systray.AddMenuItem(localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "StatusOff"}), "Switch off battery protection")
Expand All @@ -48,6 +50,15 @@ func onReady() {
mFnlock := systray.AddMenuItem("", "")
systray.AddSeparator()
mQuit := systray.AddMenuItem(localizer.MustLocalize(&i18n.LocalizeConfig{DefaultMessage: &i18n.Message{ID: "Quit", Other: "Quit"}}), "Quit the applet")
if config.kdblightTimeout == nil {
mKbdlightTimeout.Hide()
logTrace.Println("no access to kbdlight_timeout setting, not showing its GUI")
} else {
mKbdlightTimeout.SetTitle(getKbdlightTimeoutStatus())
if !config.kdblightTimeout.isWritable() {
mKbdlightTimeout.Disable()
}
}
if config.thresh == nil {
mStatus.Hide()
logTrace.Println("no access to BP information, not showing it")
Expand Down Expand Up @@ -107,6 +118,7 @@ func onReady() {
if err := ui.Main(func() {
ui.OnShouldQuit(func() bool {
customWindow.Destroy()
kbdlightTimeoutWindow.Destroy()
logTrace.Println("ready to quit GUI thread")
return true
})
Expand All @@ -119,6 +131,12 @@ func onReady() {
ui.QueueMain(func() { customThresholds(ch) })
<-ch
mStatus.SetTitle(getStatus())
case <-mKbdlightTimeout.ClickedCh:
logTrace.Println("Got a click on KbdlightTimeout")
ch := make(chan struct{})
ui.QueueMain(func() { kbdlightTimeout(ch) })
<-ch
mKbdlightTimeout.SetTitle(getKbdlightTimeoutStatus())
case <-appQuit:
return
}
Expand Down
117 changes: 110 additions & 7 deletions driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ import (
)

const (
fnlockDriverEndpoint = "/sys/devices/platform/huawei-wmi/fn_lock_state"
threshKernelPath = "/sys/class/power_supply/BAT"
threshKernelMin = "/charge_control_start_threshold"
threshKernelMax = "/charge_control_end_threshold"
saveValuesPath = "/etc/default/huawei-wmi/"
kbdlightTimeoutDriverEndpoint = "/sys/devices/platform/huawei-wmi/kbdlight_timeout"
fnlockDriverEndpoint = "/sys/devices/platform/huawei-wmi/fn_lock_state"
threshKernelPath = "/sys/class/power_supply/BAT"
threshKernelMin = "/charge_control_start_threshold"
threshKernelMax = "/charge_control_end_threshold"
saveValuesPath = "/etc/default/huawei-wmi/"
)

var (
Expand All @@ -43,8 +44,11 @@ var (
{threshDriverSingle{path: (saveValuesPath + "charge_control_thresholds")}},
{threshDriverSingle{path: (saveValuesPath + "charge_thresholds")}},
}
threshDriver1 = threshDriver{threshDriverSingle{path: "/sys/devices/platform/huawei-wmi/charge_thresholds"}}
threshDriver2 = threshDriver{threshDriverSingle{path: "/sys/devices/platform/huawei-wmi/charge_control_thresholds"}}
threshDriver1 = threshDriver{threshDriverSingle{path: "/sys/devices/platform/huawei-wmi/charge_thresholds"}}
threshDriver2 = threshDriver{threshDriverSingle{path: "/sys/devices/platform/huawei-wmi/charge_control_thresholds"}}
kdblightTimeoutEndpoints = []kdblightTimeoutEndpoint{
kdblightTimeoutDriver{path: kbdlightTimeoutDriverEndpoint},
}
)

func init() {
Expand Down Expand Up @@ -118,6 +122,74 @@ type threshScript struct {
offCmd *exec.Cmd
}

type kdblightTimeoutEndpoint interface {
set(int)
get() (int, error)
isWritable() bool
}

type kdblightTimeoutDriver struct {
path string
}

func (drv kdblightTimeoutDriver) set(i int) {
if i < 0 {
return
}
err := ioutil.WriteFile(drv.path, []byte(strconv.Itoa(i)), 0664)
if err != nil {
logError.Println(localizer.MustLocalize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "CantSetKdblightTimeout",
Other: "Failed to set keyboard light timeout",
},
}))
}
}

func (drv kdblightTimeoutDriver) get() (int, error) {
if _, err := os.Stat(drv.path); err != nil {
logTrace.Printf("Couldn't access %q.", drv.path)
return 0, err
}

val, err := ioutil.ReadFile(drv.path)
if err != nil {
logError.Println(localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "CantReadKdblightTimeout"}))
logTrace.Println(err)
return 0, err
}
result, err := strconv.Atoi(strings.TrimSpace(string(val)))
if err != nil || result < 0 {
logWarning.Println(localizer.MustLocalize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "StrangeKdblightTimeout",
Other: "Keyboard light timeout reported by driver doesn't make sense",
},
}))
}
return result, err
}

func (drv kdblightTimeoutDriver) isWritable() bool {
val, err := drv.get()
if err == nil {
err = ioutil.WriteFile(drv.path, []byte(strconv.Itoa(val)), 0664)
if err == nil {
logTrace.Println("successful write to driver interface")
return true
}
}
logTrace.Println(err)
logWarning.Println(localizer.MustLocalize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "ReadOnlyDriver",
Other: "Driver interface is readable but not writeable.",
},
}))
return false
}

func (drv fnlockDriver) get() (bool, error) {
if _, err := os.Stat(drv.path); err != nil {
logTrace.Printf("Couldn't access %q.", drv.path)
Expand Down Expand Up @@ -444,6 +516,37 @@ func getFnlockStatus() string {
return r
}

func getKbdlightTimeoutStatus() string {
var r string
timeout, err := config.kdblightTimeout.get()
if err != nil {
r = localizer.MustLocalize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "KdblightTimeoutStatusError",
Other: "ERROR: Keyboard light timeout unknown",
},
})
} else if timeout == 0 {
r = localizer.MustLocalize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "KdblightTimeoutStatusOff",
Other: "Keyboard light timeout is off",
},
})
} else {
r = localizer.MustLocalize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "KdblightTimeoutStatusOn",
One: "Keyboard light timeout is {{.Timeout}}s.",
Other: "Keyboard light timeout is {{.Timeout}}s.",
},
TemplateData: map[string]interface{}{"Timeout": timeout},
PluralCount: timeout,
})
}
return r
}

func parseOnOffStatus(s string) string {
stateRe := regexp.MustCompile(`^o(n|ff)$`)
lines := strings.Split(s, "\n")
Expand Down
34 changes: 28 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,13 @@ var (
noSaveValues bool
localizer *i18n.Localizer
config struct {
fnlock fnlockEndpoint
thresh threshEndpoint
threshPers threshEndpoint
wait bool
useScripts bool
windowed bool
fnlock fnlockEndpoint
thresh threshEndpoint
threshPers threshEndpoint
kdblightTimeout kdblightTimeoutEndpoint
wait bool
useScripts bool
windowed bool
}
)

Expand All @@ -64,6 +65,7 @@ func main() {

findFnlock()
findThresh()
findKdblightTimeout()

if saveValues {
logWarning.Println(localizer.MustLocalize(&i18n.LocalizeConfig{DefaultMessage: &i18n.Message{ID: "OptionSDeprecated", Other: "-s option is deprecated, applet is now saving values for persistence by default"}}))
Expand Down Expand Up @@ -124,6 +126,26 @@ func findThresh() {
}
}

// findKdblightTimeout finds working kdblight_timeout interface (if any)
func findKdblightTimeout() {
for _, kdblightTimeout := range kdblightTimeoutEndpoints {
_, err := kdblightTimeout.get()
if err != nil {
continue
}
config.kdblightTimeout = kdblightTimeout
if kdblightTimeout.isWritable() {
logInfo.Println(localizer.MustLocalize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "FoundKdblightTimeout",
Other: "Found writable kdblight_timeout endpoint, will use it",
},
}))
break
}
}
}

func parseFlags() {
verbose := flag.Bool("v", false, localizer.MustLocalize(&i18n.LocalizeConfig{DefaultMessage: &i18n.Message{ID: "FlagV", Other: "be verbose"}}))
verboseMore := flag.Bool("vv", false, localizer.MustLocalize(&i18n.LocalizeConfig{DefaultMessage: &i18n.Message{ID: "FlagVV", Other: "be very verbose"}}))
Expand Down
91 changes: 89 additions & 2 deletions ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ import (
)

var (
customWindow *ui.Window
mainWindow *ui.Window
kbdlightTimeoutWindow *ui.Window
customWindow *ui.Window
mainWindow *ui.Window
)

func launchUI() {
Expand All @@ -43,6 +44,42 @@ func launchUI() {
vbox.SetPadded(true)
mainWindow.SetChild(vbox)

kbdlightTimeoutGroup := ui.NewGroup("")
kbdlightTimeoutGroup.SetMargined(true)
if config.kdblightTimeout == nil {
logTrace.Println("no access to kbdlight_timeout setting, not showing its GUI")
} else {
vbox.Append(kbdlightTimeoutGroup, false)
kbdlightTimeoutGroup.SetTitle(getKbdlightTimeoutStatus())
kbdlightTimeoutVbox := ui.NewVerticalBox()
kbdlightTimeoutVbox.SetPadded(true)
kbdlightTimeoutGroup.SetChild(kbdlightTimeoutVbox)

kbdlightTimeoutButton := ui.NewButton(localizer.MustLocalize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{ID: "ChangeValue", Other: "Change"},
}))

var kbdlightTimeoutOnClicked func(*ui.Button)
kbdlightTimeoutOnClicked = func(*ui.Button) {
logTrace.Println("Custom button clicked")
go func() {
kbdlightTimeoutButton.OnClicked(func(*ui.Button) {})
ch := make(chan struct{})
ui.QueueMain(func() { kbdlightTimeout(ch) })
<-ch
kbdlightTimeoutGroup.SetTitle(getKbdlightTimeoutStatus())
kbdlightTimeoutButton.OnClicked(kbdlightTimeoutOnClicked)
}()
}
kbdlightTimeoutButton.OnClicked(kbdlightTimeoutOnClicked)

kbdlightTimeoutVbox.Append(kbdlightTimeoutButton, false)

if !config.kdblightTimeout.isWritable() {
kbdlightTimeoutButton.Disable()
}
}

batteryGroup := ui.NewGroup("")
batteryGroup.SetMargined(true)
if config.thresh == nil {
Expand Down Expand Up @@ -183,3 +220,53 @@ func customThresholds(ch chan struct{}) {
maxSlider.SetValue(max)
customWindow.Show()
}

func kbdlightTimeout(ch chan struct{}) {
logTrace.Println("Launching custom kdblight_timeout window")
timeout, _ := config.kdblightTimeout.get()

kbdlightTimeoutWindow = ui.NewWindow(localizer.MustLocalize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "KbdlightTimeoutWindowTitle",
Other: "Keyboard Light Timeout",
},
}), 640, 100, false)
kbdlightTimeoutWindow.OnClosing(func(*ui.Window) bool {
close(ch)
return true
})
kbdlightTimeoutWindow.SetMargined(true)
vbox := ui.NewVerticalBox()
vbox.SetPadded(true)
hbox := ui.NewHorizontalBox()
hbox.SetPadded(true)
kbdlightTimeoutWindow.SetChild(vbox)

timeoutLabel := ui.NewLabel(localizer.MustLocalize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "KbdlightTimeoutExplain",
Other: "Keyboard light will stay on for this number of seconds when enabled. 0 = forever.",
},
}))
vbox.Append(timeoutLabel, false)

timeoutSpinbox := ui.NewSpinbox(0, 24*60*60)
timeoutSpinbox.SetValue(timeout)
vbox.Append(timeoutSpinbox, false)

setButton := ui.NewButton(localizer.MustLocalize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "DoSet",
Other: "Set",
},
}))
setButton.OnClicked(func(*ui.Button) {
config.kdblightTimeout.set(timeoutSpinbox.Value())
kbdlightTimeoutWindow.Destroy()
close(ch)
})

vbox.Append(hbox, false)
hbox.Append(setButton, true)
kbdlightTimeoutWindow.Show()
}

0 comments on commit 31b7615

Please sign in to comment.