mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2025-03-05 20:49:04 -08:00
parent
253c71e0cf
commit
17857db340
60
src/environment/battery/battery.go
Normal file
60
src/environment/battery/battery.go
Normal file
|
@ -0,0 +1,60 @@
|
|||
// Copyright (C) 2016-2017 Karol 'Kenji Takahashi' Woźniak
|
||||
//
|
||||
// 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.
|
||||
|
||||
package battery
|
||||
|
||||
type Info struct {
|
||||
Percentage int
|
||||
State State
|
||||
}
|
||||
|
||||
type NoBatteryError struct{}
|
||||
|
||||
func (m *NoBatteryError) Error() string {
|
||||
return "no battery"
|
||||
}
|
||||
|
||||
// State type enumerates possible battery states.
|
||||
type State int
|
||||
|
||||
var states = [...]string{
|
||||
Unknown: "Unknown",
|
||||
Empty: "Empty",
|
||||
Full: "Full",
|
||||
Charging: "Charging",
|
||||
Discharging: "Discharging",
|
||||
NotCharging: "Not Charging",
|
||||
}
|
||||
|
||||
func (s State) String() string {
|
||||
return states[s]
|
||||
}
|
||||
|
||||
// Possible state values.
|
||||
// Unknown can mean either controller returned unknown, or
|
||||
// not able to retrieve state due to some error.
|
||||
const (
|
||||
Unknown State = iota
|
||||
Empty
|
||||
Full
|
||||
Charging
|
||||
Discharging
|
||||
NotCharging
|
||||
)
|
56
src/environment/battery/battery_darwin.go
Normal file
56
src/environment/battery/battery_darwin.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package battery
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"oh-my-posh/environment/cmd"
|
||||
"oh-my-posh/regex"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func mapMostLogicalState(state string) State {
|
||||
switch state {
|
||||
case "charging":
|
||||
return Charging
|
||||
case "discharging":
|
||||
return Discharging
|
||||
case "AC attached":
|
||||
return NotCharging
|
||||
case "full":
|
||||
return Full
|
||||
case "empty":
|
||||
return Empty
|
||||
case "charged":
|
||||
return Full
|
||||
default:
|
||||
return Unknown
|
||||
}
|
||||
}
|
||||
|
||||
func parseBatteryOutput(output string) (*Info, error) {
|
||||
matches := regex.FindNamedRegexMatch(`(?P<PERCENTAGE>[0-9]{1,3})%; (?P<STATE>[a-zA-Z\s]+);`, output)
|
||||
if len(matches) != 2 {
|
||||
msg := "Unable to find battery state based on output"
|
||||
return nil, errors.New(msg)
|
||||
}
|
||||
var percentage int
|
||||
var err error
|
||||
if percentage, err = strconv.Atoi(matches["PERCENTAGE"]); err != nil {
|
||||
return nil, errors.New("Unable to parse battery percentage")
|
||||
}
|
||||
return &Info{
|
||||
Percentage: percentage,
|
||||
State: mapMostLogicalState(matches["STATE"]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Get() (*Info, error) {
|
||||
output, err := cmd.Run("pmset", "-g", "batt")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !strings.Contains(output, "Battery") {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
return parseBatteryOutput(output)
|
||||
}
|
|
@ -1,11 +1,8 @@
|
|||
//go:build darwin
|
||||
|
||||
package environment
|
||||
package battery
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/distatus/battery"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -13,44 +10,43 @@ func TestParseBatteryOutput(t *testing.T) {
|
|||
cases := []struct {
|
||||
Case string
|
||||
Output string
|
||||
ExpectedState battery.State
|
||||
ExpectedState State
|
||||
ExpectedPercentage int
|
||||
ExpectError bool
|
||||
}{
|
||||
{
|
||||
Case: "charging",
|
||||
Output: "99%; charging;",
|
||||
ExpectedState: battery.Charging,
|
||||
ExpectedState: Charging,
|
||||
ExpectedPercentage: 99,
|
||||
},
|
||||
{
|
||||
Case: "charging 1%",
|
||||
Output: "1%; charging;",
|
||||
ExpectedState: battery.Charging,
|
||||
ExpectedState: Charging,
|
||||
ExpectedPercentage: 1,
|
||||
},
|
||||
{
|
||||
Case: "not charging 80%",
|
||||
Output: "81%; AC attached;",
|
||||
ExpectedState: battery.NotCharging,
|
||||
ExpectedState: NotCharging,
|
||||
ExpectedPercentage: 81,
|
||||
},
|
||||
{
|
||||
Case: "charged",
|
||||
Output: "100%; charged;",
|
||||
ExpectedState: battery.Full,
|
||||
ExpectedState: Full,
|
||||
ExpectedPercentage: 100,
|
||||
},
|
||||
{
|
||||
Case: "discharging",
|
||||
Output: "100%; discharging;",
|
||||
ExpectedState: battery.Discharging,
|
||||
ExpectedState: Discharging,
|
||||
ExpectedPercentage: 100,
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
env := ShellEnvironment{}
|
||||
info, err := env.parseBatteryOutput(tc.Output)
|
||||
info, err := parseBatteryOutput(tc.Output)
|
||||
if tc.ExpectError {
|
||||
assert.Error(t, err, tc.Case)
|
||||
return
|
156
src/environment/battery/battery_linux.go
Normal file
156
src/environment/battery/battery_linux.go
Normal file
|
@ -0,0 +1,156 @@
|
|||
// battery
|
||||
// Copyright (C) 2016-2017 Karol 'Kenji Takahashi' Woźniak
|
||||
//
|
||||
// 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.
|
||||
|
||||
package battery
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const sysfs = "/sys/class/power_supply"
|
||||
|
||||
func newState(name string) (State, error) {
|
||||
for i, state := range states {
|
||||
if strings.EqualFold(name, state) {
|
||||
return State(i), nil
|
||||
}
|
||||
}
|
||||
return Unknown, fmt.Errorf("Invalid state `%s`", name)
|
||||
}
|
||||
|
||||
func readFloat(path, filename string) (float64, error) {
|
||||
str, err := ioutil.ReadFile(filepath.Join(path, filename))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(str) == 0 {
|
||||
return 0, ErrNotFound
|
||||
}
|
||||
num, err := strconv.ParseFloat(string(str[:len(str)-1]), 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return num / 1000, nil // Convert micro->milli
|
||||
}
|
||||
|
||||
func readAmp(path, filename string, volts float64) (float64, error) {
|
||||
val, err := readFloat(path, filename)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return val * volts, nil
|
||||
}
|
||||
|
||||
func isBattery(path string) bool {
|
||||
t, err := ioutil.ReadFile(filepath.Join(path, "type"))
|
||||
return err == nil && string(t) == "Battery\n"
|
||||
}
|
||||
|
||||
func getBatteryFiles() ([]string, error) {
|
||||
files, err := ioutil.ReadDir(sysfs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var bFiles []string
|
||||
for _, file := range files {
|
||||
path := filepath.Join(sysfs, file.Name())
|
||||
if isBattery(path) {
|
||||
bFiles = append(bFiles, path)
|
||||
}
|
||||
}
|
||||
|
||||
if len(bFiles) == 0 {
|
||||
return nil, &NoBatteryError{}
|
||||
}
|
||||
|
||||
return bFiles, nil
|
||||
}
|
||||
|
||||
func getByPath(path string) (*battery, error) {
|
||||
b := &battery{}
|
||||
var err error
|
||||
|
||||
if b.Current, err = readFloat(path, "energy_now"); err == nil {
|
||||
if b.Full, err = readFloat(path, "energy_full"); err != nil {
|
||||
return nil, errors.New("unable to parse energy_full")
|
||||
}
|
||||
} else {
|
||||
currentDoesNotExist := os.IsNotExist(err)
|
||||
if b.Voltage, err = readFloat(path, "voltage_now"); err != nil {
|
||||
return nil, errors.New("unable to parse voltage_now")
|
||||
}
|
||||
b.Voltage /= 1000
|
||||
if currentDoesNotExist {
|
||||
if b.Current, err = readAmp(path, "charge_now", b.Voltage); err != nil {
|
||||
return nil, errors.New("unable to parse charge_now")
|
||||
}
|
||||
if b.Full, err = readAmp(path, "charge_full", b.Voltage); err != nil {
|
||||
return nil, errors.New("unable to parse charge_full")
|
||||
}
|
||||
} else {
|
||||
if b.Full, err = readFloat(path, "energy_full"); err != nil {
|
||||
return nil, errors.New("unable to parse energy_full")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state, err := ioutil.ReadFile(filepath.Join(path, "status"))
|
||||
if err != nil || len(state) == 0 {
|
||||
return nil, errors.New("unable to parse or invalid status")
|
||||
}
|
||||
if b.State, err = newState(string(state[:len(state)-1])); err != nil {
|
||||
return nil, errors.New("unable to map to new state")
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func systemGetAll() ([]*battery, error) {
|
||||
bFiles, err := getBatteryFiles()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var batteries []*battery
|
||||
var errs Errors
|
||||
|
||||
for _, bFile := range bFiles {
|
||||
b, err := getByPath(bFile)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
batteries = append(batteries, b)
|
||||
}
|
||||
|
||||
if len(batteries) == 0 {
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
return batteries, nil
|
||||
}
|
321
src/environment/battery/battery_windows.go
Normal file
321
src/environment/battery/battery_windows.go
Normal file
|
@ -0,0 +1,321 @@
|
|||
// battery
|
||||
// Copyright (C) 2016-2017 Karol 'Kenji Takahashi' Woźniak
|
||||
//
|
||||
// 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.
|
||||
|
||||
package battery
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type batteryQueryInformation struct {
|
||||
BatteryTag uint32
|
||||
InformationLevel int32
|
||||
AtRate int32
|
||||
}
|
||||
|
||||
type batteryInformation struct {
|
||||
Capabilities uint32
|
||||
Technology uint8
|
||||
Reserved [3]uint8
|
||||
Chemistry [4]uint8
|
||||
DesignedCapacity uint32
|
||||
FullChargedCapacity uint32
|
||||
DefaultAlert1 uint32
|
||||
DefaultAlert2 uint32
|
||||
CriticalBias uint32
|
||||
CycleCount uint32
|
||||
}
|
||||
|
||||
type batteryWaitStatus struct {
|
||||
BatteryTag uint32
|
||||
Timeout uint32
|
||||
PowerState uint32
|
||||
LowCapacity uint32
|
||||
HighCapacity uint32
|
||||
}
|
||||
|
||||
type batteryStatus struct {
|
||||
PowerState uint32
|
||||
Capacity uint32
|
||||
Voltage uint32
|
||||
Rate int32
|
||||
}
|
||||
|
||||
type guid struct {
|
||||
Data1 uint32
|
||||
Data2 uint16
|
||||
Data3 uint16
|
||||
Data4 [8]byte
|
||||
}
|
||||
|
||||
type spDeviceInterfaceData struct {
|
||||
cbSize uint32
|
||||
InterfaceClassGuid guid // nolint:revive
|
||||
Flags uint32
|
||||
Reserved uint
|
||||
}
|
||||
|
||||
var guidDeviceBattery = guid{
|
||||
0x72631e54,
|
||||
0x78A4,
|
||||
0x11d0,
|
||||
[8]byte{0xbc, 0xf7, 0x00, 0xaa, 0x00, 0xb7, 0xb3, 0x2a},
|
||||
}
|
||||
|
||||
func uint32ToFloat64(num uint32) (float64, error) {
|
||||
if num == 0xffffffff { // BATTERY_UNKNOWN_CAPACITY
|
||||
return 0, errors.New("Unknown value received")
|
||||
}
|
||||
return float64(num), nil
|
||||
}
|
||||
|
||||
func setupDiSetup(proc *windows.LazyProc, nargs, a1, a2, a3, a4, a5, a6 uintptr) (uintptr, error) {
|
||||
r1, _, errno := syscall.Syscall6(proc.Addr(), nargs, a1, a2, a3, a4, a5, a6)
|
||||
if windows.Handle(r1) == windows.InvalidHandle {
|
||||
if errno != 0 {
|
||||
return 0, error(errno)
|
||||
}
|
||||
return 0, syscall.EINVAL
|
||||
}
|
||||
return r1, nil
|
||||
}
|
||||
|
||||
func setupDiCall(proc *windows.LazyProc, nargs, a1, a2, a3, a4, a5, a6 uintptr) syscall.Errno {
|
||||
r1, _, errno := syscall.Syscall6(proc.Addr(), nargs, a1, a2, a3, a4, a5, a6)
|
||||
if r1 == 0 {
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
return syscall.EINVAL
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var setupapi = &windows.LazyDLL{Name: "setupapi.dll", System: true}
|
||||
var setupDiGetClassDevsW = setupapi.NewProc("SetupDiGetClassDevsW")
|
||||
var setupDiEnumDeviceInterfaces = setupapi.NewProc("SetupDiEnumDeviceInterfaces")
|
||||
var setupDiGetDeviceInterfaceDetailW = setupapi.NewProc("SetupDiGetDeviceInterfaceDetailW")
|
||||
var setupDiDestroyDeviceInfoList = setupapi.NewProc("SetupDiDestroyDeviceInfoList")
|
||||
|
||||
func readState(powerState uint32) State {
|
||||
switch {
|
||||
case powerState&0x00000004 != 0:
|
||||
return Charging
|
||||
case powerState&0x00000008 != 0:
|
||||
return Empty
|
||||
case powerState&0x00000002 != 0:
|
||||
return Discharging
|
||||
case powerState&0x00000001 != 0:
|
||||
return Full
|
||||
default:
|
||||
return Unknown
|
||||
}
|
||||
}
|
||||
|
||||
func systemGet(idx int) (*battery, error) {
|
||||
hdev, err := setupDiSetup(
|
||||
setupDiGetClassDevsW,
|
||||
4,
|
||||
uintptr(unsafe.Pointer(&guidDeviceBattery)),
|
||||
0,
|
||||
0,
|
||||
2|16, // DIGCF_PRESENT|DIGCF_DEVICEINTERFACE
|
||||
0, 0,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
_, _, _ = syscall.Syscall(setupDiDestroyDeviceInfoList.Addr(), 1, hdev, 0, 0)
|
||||
}()
|
||||
|
||||
var did spDeviceInterfaceData
|
||||
did.cbSize = uint32(unsafe.Sizeof(did))
|
||||
errno := setupDiCall(
|
||||
setupDiEnumDeviceInterfaces,
|
||||
5,
|
||||
hdev,
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&guidDeviceBattery)),
|
||||
uintptr(idx),
|
||||
uintptr(unsafe.Pointer(&did)),
|
||||
0,
|
||||
)
|
||||
if errno == 259 { // ERROR_NO_MORE_ITEMS
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
if errno != 0 {
|
||||
return nil, errno
|
||||
}
|
||||
var cbRequired uint32
|
||||
errno = setupDiCall(
|
||||
setupDiGetDeviceInterfaceDetailW,
|
||||
6,
|
||||
hdev,
|
||||
uintptr(unsafe.Pointer(&did)),
|
||||
0,
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&cbRequired)),
|
||||
0,
|
||||
)
|
||||
if errno != 0 && errno != 122 { // ERROR_INSUFFICIENT_BUFFER
|
||||
return nil, errno
|
||||
}
|
||||
// The god damn struct with ANYSIZE_ARRAY of utf16 in it is crazy.
|
||||
// So... let's emulate it with array of uint16 ;-D.
|
||||
// Keep in mind that the first two elements are actually cbSize.
|
||||
didd := make([]uint16, cbRequired/2)
|
||||
cbSize := (*uint32)(unsafe.Pointer(&didd[0]))
|
||||
if unsafe.Sizeof(uint(0)) == 8 {
|
||||
*cbSize = 8
|
||||
} else {
|
||||
*cbSize = 6
|
||||
}
|
||||
errno = setupDiCall(
|
||||
setupDiGetDeviceInterfaceDetailW,
|
||||
6,
|
||||
hdev,
|
||||
uintptr(unsafe.Pointer(&did)),
|
||||
uintptr(unsafe.Pointer(&didd[0])),
|
||||
uintptr(cbRequired),
|
||||
uintptr(unsafe.Pointer(&cbRequired)),
|
||||
0,
|
||||
)
|
||||
if errno != 0 {
|
||||
return nil, errno
|
||||
}
|
||||
devicePath := &didd[2:][0]
|
||||
|
||||
handle, err := windows.CreateFile(
|
||||
devicePath,
|
||||
windows.GENERIC_READ|windows.GENERIC_WRITE,
|
||||
windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE,
|
||||
nil,
|
||||
windows.OPEN_EXISTING,
|
||||
windows.FILE_ATTRIBUTE_NORMAL,
|
||||
0,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
_ = windows.CloseHandle(handle)
|
||||
}()
|
||||
|
||||
var dwOut uint32
|
||||
|
||||
var dwWait uint32
|
||||
var bqi batteryQueryInformation
|
||||
err = windows.DeviceIoControl(
|
||||
handle,
|
||||
2703424, // IOCTL_BATTERY_QUERY_TAG
|
||||
(*byte)(unsafe.Pointer(&dwWait)),
|
||||
uint32(unsafe.Sizeof(dwWait)),
|
||||
(*byte)(unsafe.Pointer(&bqi.BatteryTag)),
|
||||
uint32(unsafe.Sizeof(bqi.BatteryTag)),
|
||||
&dwOut,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bqi.BatteryTag == 0 {
|
||||
return nil, errors.New("BatteryTag not returned")
|
||||
}
|
||||
|
||||
b := &battery{}
|
||||
|
||||
var bi batteryInformation
|
||||
err = windows.DeviceIoControl(
|
||||
handle,
|
||||
2703428, // IOCTL_BATTERY_QUERY_INFORMATION
|
||||
(*byte)(unsafe.Pointer(&bqi)),
|
||||
uint32(unsafe.Sizeof(bqi)),
|
||||
(*byte)(unsafe.Pointer(&bi)),
|
||||
uint32(unsafe.Sizeof(bi)),
|
||||
&dwOut,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b.Full = float64(bi.FullChargedCapacity)
|
||||
|
||||
bws := batteryWaitStatus{BatteryTag: bqi.BatteryTag}
|
||||
var bs batteryStatus
|
||||
err = windows.DeviceIoControl(
|
||||
handle,
|
||||
2703436, // IOCTL_BATTERY_QUERY_STATUS
|
||||
(*byte)(unsafe.Pointer(&bws)),
|
||||
uint32(unsafe.Sizeof(bws)),
|
||||
(*byte)(unsafe.Pointer(&bs)),
|
||||
uint32(unsafe.Sizeof(bs)),
|
||||
&dwOut,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if b.Current, err = uint32ToFloat64(bs.Capacity); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if b.Voltage, err = uint32ToFloat64(bs.Voltage); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.Voltage /= 1000
|
||||
b.State = readState(bs.PowerState)
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func systemGetAll() ([]*battery, error) {
|
||||
var batteries []*battery
|
||||
var i int
|
||||
var errs Errors
|
||||
|
||||
for i = 0; ; i++ {
|
||||
b, err := systemGet(i)
|
||||
if err == ErrNotFound {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
batteries = append(batteries, b)
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
return nil, &NoBatteryError{}
|
||||
}
|
||||
|
||||
if len(batteries) == 0 {
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
return batteries, nil
|
||||
}
|
65
src/environment/battery/battery_windows_nix.go
Normal file
65
src/environment/battery/battery_windows_nix.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
//go:build !darwin
|
||||
|
||||
package battery
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// battery type represents a single battery entry information.
|
||||
type battery struct {
|
||||
// Current battery state.
|
||||
State State
|
||||
// Current (momentary) capacity (in mWh).
|
||||
Current float64
|
||||
// Last known full capacity (in mWh).
|
||||
Full float64
|
||||
// Current voltage (in V).
|
||||
Voltage float64
|
||||
}
|
||||
|
||||
func mapMostLogicalState(currentState, newState State) State {
|
||||
switch currentState {
|
||||
case Discharging, NotCharging:
|
||||
return Discharging
|
||||
case Empty:
|
||||
return newState
|
||||
case Charging:
|
||||
if newState == Discharging {
|
||||
return Discharging
|
||||
}
|
||||
return Charging
|
||||
case Unknown:
|
||||
return newState
|
||||
case Full:
|
||||
return newState
|
||||
}
|
||||
return newState
|
||||
}
|
||||
|
||||
// GetAll returns information about all batteries in the system.
|
||||
//
|
||||
// If error != nil, it will be either ErrFatal or Errors.
|
||||
// If error is of type Errors, it is guaranteed that length of both returned slices is the same and that i-th error coresponds with i-th battery structure.
|
||||
func Get() (*Info, error) {
|
||||
parseBatteryInfo := func(batteries []*battery) *Info {
|
||||
var info Info
|
||||
var current, total float64
|
||||
var state State
|
||||
for _, bt := range batteries {
|
||||
current += bt.Current
|
||||
total += bt.Full
|
||||
state = mapMostLogicalState(state, bt.State)
|
||||
}
|
||||
batteryPercentage := current / total * 100
|
||||
info.Percentage = int(math.Min(100, batteryPercentage))
|
||||
info.State = state
|
||||
return &info
|
||||
}
|
||||
|
||||
batteries, err := systemGetAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parseBatteryInfo(batteries), nil
|
||||
}
|
51
src/environment/battery/battery_windows_nix_test.go
Normal file
51
src/environment/battery/battery_windows_nix_test.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
//go:build !darwin
|
||||
|
||||
// battery
|
||||
// Copyright (C) 2016-2017 Karol 'Kenji Takahashi' Woźniak
|
||||
//
|
||||
// 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.
|
||||
|
||||
package battery
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/alecthomas/assert"
|
||||
)
|
||||
|
||||
func TestMapBatteriesState(t *testing.T) {
|
||||
cases := []struct {
|
||||
Case string
|
||||
ExpectedState State
|
||||
CurrentState State
|
||||
NewState State
|
||||
}{
|
||||
{Case: "charging > charged", ExpectedState: Charging, CurrentState: Full, NewState: Charging},
|
||||
{Case: "charging < discharging", ExpectedState: Discharging, CurrentState: Discharging, NewState: Charging},
|
||||
{Case: "charging == charging", ExpectedState: Charging, CurrentState: Charging, NewState: Charging},
|
||||
{Case: "discharging > charged", ExpectedState: Discharging, CurrentState: Full, NewState: Discharging},
|
||||
{Case: "discharging > unknown", ExpectedState: Discharging, CurrentState: Unknown, NewState: Discharging},
|
||||
{Case: "discharging > full", ExpectedState: Discharging, CurrentState: Full, NewState: Discharging},
|
||||
{Case: "discharging > charging 2", ExpectedState: Discharging, CurrentState: Charging, NewState: Discharging},
|
||||
{Case: "discharging > empty", ExpectedState: Discharging, CurrentState: Empty, NewState: Discharging},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
assert.Equal(t, tc.ExpectedState, mapMostLogicalState(tc.CurrentState, tc.NewState), tc.Case)
|
||||
}
|
||||
}
|
42
src/environment/battery/errors.go
Normal file
42
src/environment/battery/errors.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
// battery
|
||||
// Copyright (C) 2016-2017 Karol 'Kenji Takahashi' Woźniak
|
||||
//
|
||||
// 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.
|
||||
|
||||
package battery
|
||||
|
||||
import "fmt"
|
||||
|
||||
var ErrNotFound = fmt.Errorf("Not found")
|
||||
|
||||
type Errors []error
|
||||
|
||||
func (e Errors) Error() string {
|
||||
var s string
|
||||
for _, err := range e {
|
||||
if err != nil {
|
||||
s += err.Error() + ", "
|
||||
}
|
||||
}
|
||||
// strip trailing colon/space
|
||||
if len(s) > 1 {
|
||||
s = s[:len(s)-2]
|
||||
}
|
||||
return s
|
||||
}
|
48
src/environment/battery/errors_test.go
Normal file
48
src/environment/battery/errors_test.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
// battery
|
||||
// Copyright (C) 2016-2017 Karol 'Kenji Takahashi' Woźniak
|
||||
//
|
||||
// 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.
|
||||
|
||||
package battery
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestErrors(t *testing.T) {
|
||||
cases := []struct {
|
||||
in Errors
|
||||
str string
|
||||
}{
|
||||
{Errors{nil}, ""},
|
||||
{Errors{errors.New("")}, ""},
|
||||
{Errors{errors.New("t1")}, "t1"},
|
||||
{Errors{errors.New("t2"), errors.New("t3")}, "t2, t3"},
|
||||
{Errors{errors.New("t4"), errors.New("t5")}, "t4, t5"},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
str := c.in.Error()
|
||||
|
||||
if str != c.str {
|
||||
t.Errorf("%d: %v != %v", i, str, c.str)
|
||||
}
|
||||
}
|
||||
}
|
27
src/environment/cmd/run.go
Normal file
27
src/environment/cmd/run.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Run(command string, args ...string) (string, error) {
|
||||
cmd := exec.Command(command, args...)
|
||||
var out bytes.Buffer
|
||||
var err bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = &err
|
||||
cmdErr := cmd.Run()
|
||||
if cmdErr != nil {
|
||||
output := err.String()
|
||||
return output, cmdErr
|
||||
}
|
||||
// some silly commands return 0 and the output is in stderr instead of stdout
|
||||
result := out.String()
|
||||
if len(result) == 0 {
|
||||
result = err.String()
|
||||
}
|
||||
output := strings.TrimSpace(result)
|
||||
return output, nil
|
||||
}
|
|
@ -9,6 +9,8 @@ import (
|
|||
"io/fs"
|
||||
"log"
|
||||
"net/http"
|
||||
"oh-my-posh/environment/battery"
|
||||
"oh-my-posh/environment/cmd"
|
||||
"oh-my-posh/regex"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
@ -19,7 +21,6 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/distatus/battery"
|
||||
process "github.com/shirou/gopsutil/v3/process"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
|
@ -61,12 +62,6 @@ func (e *CommandError) Error() string {
|
|||
return e.Err
|
||||
}
|
||||
|
||||
type NoBatteryError struct{}
|
||||
|
||||
func (m *NoBatteryError) Error() string {
|
||||
return "no battery"
|
||||
}
|
||||
|
||||
type FileInfo struct {
|
||||
ParentFolder string
|
||||
Path string
|
||||
|
@ -140,11 +135,6 @@ func (t *TemplateCache) AddSegmentData(key string, value interface{}) {
|
|||
t.Segments[key] = value
|
||||
}
|
||||
|
||||
type BatteryInfo struct {
|
||||
Percentage int
|
||||
State battery.State
|
||||
}
|
||||
|
||||
type Environment interface {
|
||||
Getenv(key string) string
|
||||
Pwd() string
|
||||
|
@ -173,7 +163,7 @@ type Environment interface {
|
|||
RunShellCommand(shell, command string) string
|
||||
ExecutionTime() float64
|
||||
Flags() *Flags
|
||||
BatteryState() (*BatteryInfo, error)
|
||||
BatteryState() (*battery.Info, error)
|
||||
QueryWindowTitles(processName, windowTitleRegex string) (string, error)
|
||||
WindowsRegistryKeyValue(path string) (*WindowsRegistryValue, error)
|
||||
HTTPRequest(url string, timeout int, requestModifiers ...HTTPRequestModifier) ([]byte, error)
|
||||
|
@ -203,11 +193,11 @@ func (c *commandCache) set(command, path string) {
|
|||
}
|
||||
|
||||
func (c *commandCache) get(command string) (string, bool) {
|
||||
cmd, found := c.commands.get(command)
|
||||
cacheCommand, found := c.commands.get(command)
|
||||
if !found {
|
||||
return "", false
|
||||
}
|
||||
command, ok := cmd.(string)
|
||||
command, ok := cacheCommand.(string)
|
||||
return command, ok
|
||||
}
|
||||
|
||||
|
@ -494,29 +484,15 @@ func (env *ShellEnvironment) GOOS() string {
|
|||
|
||||
func (env *ShellEnvironment) RunCommand(command string, args ...string) (string, error) {
|
||||
defer env.Trace(time.Now(), "RunCommand", append([]string{command}, args...)...)
|
||||
if cmd, ok := env.cmdCache.get(command); ok {
|
||||
command = cmd
|
||||
if cacheCommand, ok := env.cmdCache.get(command); ok {
|
||||
command = cacheCommand
|
||||
}
|
||||
cmd := exec.Command(command, args...)
|
||||
var out bytes.Buffer
|
||||
var err bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = &err
|
||||
cmdErr := cmd.Run()
|
||||
if cmdErr != nil {
|
||||
output := err.String()
|
||||
errorStr := fmt.Sprintf("cmd.Start() failed with '%s'", output)
|
||||
env.Log(Error, "RunCommand", errorStr)
|
||||
return output, cmdErr
|
||||
output, err := cmd.Run(command, args...)
|
||||
if err != nil {
|
||||
env.Log(Error, "RunCommand", "cmd.Run() failed")
|
||||
}
|
||||
// some silly commands return 0 and the output is in stderr instead of stdout
|
||||
result := out.String()
|
||||
if len(result) == 0 {
|
||||
result = err.String()
|
||||
}
|
||||
output := strings.TrimSpace(result)
|
||||
env.Log(Debug, "RunCommand", output)
|
||||
return output, nil
|
||||
return output, err
|
||||
}
|
||||
|
||||
func (env *ShellEnvironment) RunShellCommand(shell, command string) string {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
//go:build darwin
|
||||
|
||||
package environment
|
||||
|
||||
import (
|
||||
|
@ -9,7 +7,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/distatus/battery"
|
||||
"oh-my-posh/environment/battery"
|
||||
)
|
||||
|
||||
func mapMostLogicalState(state string) battery.State {
|
||||
|
@ -31,30 +29,30 @@ func mapMostLogicalState(state string) battery.State {
|
|||
}
|
||||
}
|
||||
|
||||
func (env *ShellEnvironment) parseBatteryOutput(output string) (*BatteryInfo, error) {
|
||||
func (env *ShellEnvironment) parseBatteryOutput(output string) (*battery.Info, error) {
|
||||
matches := regex.FindNamedRegexMatch(`(?P<PERCENTAGE>[0-9]{1,3})%; (?P<STATE>[a-zA-Z\s]+);`, output)
|
||||
if len(matches) != 2 {
|
||||
msg := "Unable to find battery state based on output"
|
||||
env.Log(Error, "BatteryInfo", msg)
|
||||
env.Log(Error, "BatteryState", msg)
|
||||
return nil, errors.New(msg)
|
||||
}
|
||||
var percentage int
|
||||
var err error
|
||||
if percentage, err = strconv.Atoi(matches["PERCENTAGE"]); err != nil {
|
||||
env.Log(Error, "BatteryInfo", err.Error())
|
||||
env.Log(Error, "BatteryState", err.Error())
|
||||
return nil, errors.New("Unable to parse battery percentage")
|
||||
}
|
||||
return &BatteryInfo{
|
||||
return &battery.Info{
|
||||
Percentage: percentage,
|
||||
State: mapMostLogicalState(matches["STATE"]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (env *ShellEnvironment) BatteryState() (*BatteryInfo, error) {
|
||||
defer env.Trace(time.Now(), "BatteryInfo")
|
||||
func (env *ShellEnvironment) BatteryState() (*battery.Info, error) {
|
||||
defer env.Trace(time.Now(), "BatteryState")
|
||||
output, err := env.RunCommand("pmset", "-g", "batt")
|
||||
if err != nil {
|
||||
env.Log(Error, "BatteryInfo", err.Error())
|
||||
env.Log(Error, "BatteryState", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
if !strings.Contains(output, "Battery") {
|
|
@ -1,5 +1,3 @@
|
|||
//go:build windows
|
||||
|
||||
package environment
|
||||
|
||||
import (
|
18
src/environment/shell_windows_nix.go
Normal file
18
src/environment/shell_windows_nix.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
//go:build !darwin
|
||||
|
||||
package environment
|
||||
|
||||
import (
|
||||
"oh-my-posh/environment/battery"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (env *ShellEnvironment) BatteryState() (*battery.Info, error) {
|
||||
defer env.Trace(time.Now(), "BatteryState")
|
||||
info, err := battery.Get()
|
||||
if err != nil {
|
||||
env.Log(Error, "BatteryState", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
return info, nil
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
//go:build !darwin
|
||||
|
||||
package environment
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/distatus/battery"
|
||||
)
|
||||
|
||||
func mapMostLogicalState(currentState, newState battery.State) battery.State {
|
||||
switch currentState {
|
||||
case battery.Discharging, battery.NotCharging:
|
||||
return battery.Discharging
|
||||
case battery.Empty:
|
||||
return newState
|
||||
case battery.Charging:
|
||||
if newState == battery.Discharging {
|
||||
return battery.Discharging
|
||||
}
|
||||
return battery.Charging
|
||||
case battery.Unknown:
|
||||
return newState
|
||||
case battery.Full:
|
||||
return newState
|
||||
}
|
||||
return newState
|
||||
}
|
||||
|
||||
func (env *ShellEnvironment) BatteryState() (*BatteryInfo, error) {
|
||||
defer env.Trace(time.Now(), "BatteryInfo")
|
||||
|
||||
parseBatteryInfo := func(batteries []*battery.Battery) *BatteryInfo {
|
||||
var info BatteryInfo
|
||||
var current, total float64
|
||||
var state battery.State
|
||||
for _, bt := range batteries {
|
||||
current += bt.Current
|
||||
total += bt.Full
|
||||
state = mapMostLogicalState(state, bt.State)
|
||||
}
|
||||
batteryPercentage := current / total * 100
|
||||
info.Percentage = int(math.Min(100, batteryPercentage))
|
||||
info.State = state
|
||||
return &info
|
||||
}
|
||||
|
||||
batteries, err := battery.GetAll()
|
||||
// actual error, return it
|
||||
if err != nil && len(batteries) == 0 {
|
||||
env.Log(Error, "BatteryInfo", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
// there are no batteries found
|
||||
if len(batteries) == 0 {
|
||||
return nil, &NoBatteryError{}
|
||||
}
|
||||
// some batteries fail to get retrieved, filter them out if present
|
||||
validBatteries := []*battery.Battery{}
|
||||
for _, batt := range batteries {
|
||||
if batt != nil {
|
||||
validBatteries = append(validBatteries, batt)
|
||||
}
|
||||
}
|
||||
// clean minor errors
|
||||
unableToRetrieveBatteryInfo := "A device which does not exist was specified."
|
||||
unknownChargeRate := "Unknown value received"
|
||||
var fatalErr battery.Errors
|
||||
ignoreErr := func(err error) bool {
|
||||
if e, ok := err.(battery.ErrPartial); ok {
|
||||
// ignore unknown charge rate value error
|
||||
if e.Current == nil &&
|
||||
e.Design == nil &&
|
||||
e.DesignVoltage == nil &&
|
||||
e.Full == nil &&
|
||||
e.State == nil &&
|
||||
e.Voltage == nil &&
|
||||
e.ChargeRate != nil &&
|
||||
e.ChargeRate.Error() == unknownChargeRate {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
if batErr, ok := err.(battery.Errors); ok {
|
||||
for _, err := range batErr {
|
||||
if !ignoreErr(err) {
|
||||
fatalErr = append(fatalErr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// when battery info fails to get retrieved but there is at least one valid battery, return it without error
|
||||
if len(validBatteries) > 0 && fatalErr != nil && strings.Contains(fatalErr.Error(), unableToRetrieveBatteryInfo) {
|
||||
return parseBatteryInfo(validBatteries), nil
|
||||
}
|
||||
// another error occurred (possibly unmapped use-case), return it
|
||||
if fatalErr != nil {
|
||||
env.Log(Error, "BatteryInfo", fatalErr.Error())
|
||||
return nil, fatalErr
|
||||
}
|
||||
// everything is fine
|
||||
return parseBatteryInfo(validBatteries), nil
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
//go:build !darwin
|
||||
|
||||
package environment
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/distatus/battery"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMapBatteriesState(t *testing.T) {
|
||||
cases := []struct {
|
||||
Case string
|
||||
ExpectedState battery.State
|
||||
CurrentState battery.State
|
||||
NewState battery.State
|
||||
}{
|
||||
{Case: "charging > charged", ExpectedState: battery.Charging, CurrentState: battery.Full, NewState: battery.Charging},
|
||||
{Case: "charging < discharging", ExpectedState: battery.Discharging, CurrentState: battery.Discharging, NewState: battery.Charging},
|
||||
{Case: "charging == charging", ExpectedState: battery.Charging, CurrentState: battery.Charging, NewState: battery.Charging},
|
||||
{Case: "discharging > charged", ExpectedState: battery.Discharging, CurrentState: battery.Full, NewState: battery.Discharging},
|
||||
{Case: "discharging > unknown", ExpectedState: battery.Discharging, CurrentState: battery.Unknown, NewState: battery.Discharging},
|
||||
{Case: "discharging > full", ExpectedState: battery.Discharging, CurrentState: battery.Full, NewState: battery.Discharging},
|
||||
{Case: "discharging > charging 2", ExpectedState: battery.Discharging, CurrentState: battery.Charging, NewState: battery.Discharging},
|
||||
{Case: "discharging > empty", ExpectedState: battery.Discharging, CurrentState: battery.Empty, NewState: battery.Discharging},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
assert.Equal(t, tc.ExpectedState, mapMostLogicalState(tc.CurrentState, tc.NewState), tc.Case)
|
||||
}
|
||||
}
|
|
@ -8,7 +8,6 @@ require (
|
|||
github.com/alecthomas/assert v1.0.0
|
||||
github.com/alecthomas/colour v0.1.0 // indirect
|
||||
github.com/alecthomas/repr v0.1.0 // indirect
|
||||
github.com/distatus/battery v0.10.0
|
||||
github.com/esimov/stackblur-go v1.1.0
|
||||
github.com/fogleman/gg v1.3.0
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||
|
@ -29,7 +28,6 @@ require (
|
|||
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b
|
||||
golang.org/x/text v0.3.7
|
||||
gopkg.in/ini.v1 v1.66.6
|
||||
howett.net/plist v1.0.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
|
@ -90,6 +88,4 @@ require (
|
|||
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect
|
||||
)
|
||||
|
||||
replace github.com/distatus/battery v0.10.0 => github.com/JanDeDobbeleer/battery v0.10.0-5
|
||||
|
||||
replace github.com/atotto/clipboard v0.1.4 => github.com/jandedobbeleer/clipboard v0.1.4-1
|
||||
|
|
|
@ -6,8 +6,6 @@ github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I
|
|||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/ConradIrwin/font v0.0.0-20210318200717-ce8d41cc0732 h1:0EDePskeF4vNFCk70ATaFHQzjmwXsk+VImnMJttecNU=
|
||||
github.com/ConradIrwin/font v0.0.0-20210318200717-ce8d41cc0732/go.mod h1:krTLO7JWu6g8RMxG8sl+T1Hf8W93XQacBKJmqFZ2MFY=
|
||||
github.com/JanDeDobbeleer/battery v0.10.0-5 h1:RdZlM4ioJRVrt0JFAgLASbECb7xlBifvmgwYFdwp55I=
|
||||
github.com/JanDeDobbeleer/battery v0.10.0-5/go.mod h1:STnSvFLX//eEpkaN7qWRxCWxrWOcssTDgnG4yqq9BRE=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
||||
|
@ -91,7 +89,6 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH
|
|||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jandedobbeleer/clipboard v0.1.4-1 h1:rJehm5W0a3hvjcxyB3snqLBV4yvMBBc12JyMP7ngNQw=
|
||||
github.com/jandedobbeleer/clipboard v0.1.4-1/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
|
@ -231,7 +228,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190912141932-bc967efca4b8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -271,8 +267,6 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR
|
|||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI=
|
||||
gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
@ -282,6 +276,3 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
|
||||
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
|
||||
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
||||
|
|
|
@ -3,6 +3,7 @@ package mock
|
|||
import (
|
||||
"io/fs"
|
||||
"oh-my-posh/environment"
|
||||
"oh-my-posh/environment/battery"
|
||||
"time"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
@ -122,9 +123,9 @@ func (env *MockedEnvironment) Flags() *environment.Flags {
|
|||
return arguments.Get(0).(*environment.Flags)
|
||||
}
|
||||
|
||||
func (env *MockedEnvironment) BatteryState() (*environment.BatteryInfo, error) {
|
||||
func (env *MockedEnvironment) BatteryState() (*battery.Info, error) {
|
||||
args := env.Called()
|
||||
return args.Get(0).(*environment.BatteryInfo), args.Error(1)
|
||||
return args.Get(0).(*battery.Info), args.Error(1)
|
||||
}
|
||||
|
||||
func (env *MockedEnvironment) Shell() string {
|
||||
|
|
|
@ -2,16 +2,15 @@ package segments
|
|||
|
||||
import (
|
||||
"oh-my-posh/environment"
|
||||
"oh-my-posh/environment/battery"
|
||||
"oh-my-posh/properties"
|
||||
|
||||
"github.com/distatus/battery"
|
||||
)
|
||||
|
||||
type Battery struct {
|
||||
props properties.Properties
|
||||
env environment.Environment
|
||||
|
||||
*environment.BatteryInfo
|
||||
*battery.Info
|
||||
Error string
|
||||
Icon string
|
||||
}
|
||||
|
@ -38,18 +37,18 @@ func (b *Battery) Enabled() bool {
|
|||
}
|
||||
|
||||
var err error
|
||||
b.BatteryInfo, err = b.env.BatteryState()
|
||||
b.Info, err = b.env.BatteryState()
|
||||
|
||||
if !b.enabledWhileError(err) {
|
||||
return false
|
||||
}
|
||||
|
||||
// case on computer without batteries(no error, empty array)
|
||||
if err == nil && b.BatteryInfo == nil {
|
||||
if err == nil && b.Info == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch b.BatteryInfo.State {
|
||||
switch b.Info.State {
|
||||
case battery.Discharging:
|
||||
b.Icon = b.props.GetString(DischargingIcon, "")
|
||||
case battery.NotCharging:
|
||||
|
@ -68,7 +67,7 @@ func (b *Battery) enabledWhileError(err error) bool {
|
|||
if err == nil {
|
||||
return true
|
||||
}
|
||||
if _, ok := err.(*environment.NoBatteryError); ok {
|
||||
if _, ok := err.(*battery.NoBatteryError); ok {
|
||||
return false
|
||||
}
|
||||
displayError := b.props.GetBool(properties.DisplayError, false)
|
||||
|
|
Loading…
Reference in a new issue