diff --git a/src/.golangci.yml b/src/.golangci.yml
index 4be42a19..3f3bcc0a 100644
--- a/src/.golangci.yml
+++ b/src/.golangci.yml
@@ -5,7 +5,6 @@ linters:
disable-all: true
enable:
- bodyclose
- - deadcode
- depguard
- dupl
- errcheck
@@ -28,12 +27,10 @@ linters:
- rowserrcheck
- exportloopref
- staticcheck
- - structcheck
- typecheck
- unconvert
- unparam
- unused
- - varcheck
- whitespace
- lll
linters-settings:
diff --git a/src/color/colors_unix.go b/src/color/colors_unix.go
index 1e40dd5d..56d5728a 100644
--- a/src/color/colors_unix.go
+++ b/src/color/colors_unix.go
@@ -3,12 +3,11 @@
package color
import (
- "errors"
"oh-my-posh/environment"
)
func GetAccentColor(env environment.Environment) (*RGB, error) {
- return nil, errors.New("not implemented")
+ return nil, &environment.NotImplemented{}
}
func (d *DefaultColors) SetAccentColor(env environment.Environment, defaultColor string) {
diff --git a/src/engine/segment.go b/src/engine/segment.go
index aaf01b12..37b00b1a 100644
--- a/src/engine/segment.go
+++ b/src/engine/segment.go
@@ -96,6 +96,8 @@ const (
CMAKE SegmentType = "cmake"
// CMD writes the output of a shell command
CMD SegmentType = "command"
+ // CONNECTION writes a connection's information
+ CONNECTION SegmentType = "connection"
// CRYSTAL writes the active crystal version
CRYSTAL SegmentType = "crystal"
// DART writes the active dart version
@@ -192,8 +194,6 @@ const (
UI5TOOLING SegmentType = "ui5tooling"
// WAKATIME writes tracked time spend in dev editors
WAKATIME SegmentType = "wakatime"
- // WIFI writes details about the current WIFI connection
- WIFI SegmentType = "wifi"
// WINREG queries the Windows registry.
WINREG SegmentType = "winreg"
// WITHINGS queries the Withings API.
@@ -277,6 +277,7 @@ func (segment *Segment) mapSegmentWithWriter(env environment.Environment) error
CF: &segments.Cf{},
CFTARGET: &segments.CfTarget{},
CMD: &segments.Cmd{},
+ CONNECTION: &segments.Connection{},
CRYSTAL: &segments.Crystal{},
CMAKE: &segments.Cmake{},
DART: &segments.Dart{},
@@ -326,7 +327,6 @@ func (segment *Segment) mapSegmentWithWriter(env environment.Environment) error
TIME: &segments.Time{},
UI5TOOLING: &segments.UI5Tooling{},
WAKATIME: &segments.Wakatime{},
- WIFI: &segments.Wifi{},
WINREG: &segments.WindowsRegistry{},
WITHINGS: &segments.Withings{},
YTM: &segments.Ytm{},
diff --git a/src/environment/shell.go b/src/environment/shell.go
index b1d2763f..ac7d43bb 100644
--- a/src/environment/shell.go
+++ b/src/environment/shell.go
@@ -98,20 +98,27 @@ type WindowsRegistryValue struct {
String string
}
-type WifiType string
+type NotImplemented struct{}
-type WifiInfo struct {
- SSID string
- Interface string
- RadioType WifiType
- PhysType WifiType
- Authentication WifiType
- Cipher WifiType
- Channel int
- ReceiveRate int
- TransmitRate int
- Signal int
- Error string
+func (n *NotImplemented) Error() string {
+ return "not implemented"
+}
+
+type ConnectionType string
+
+const (
+ ETHERNET ConnectionType = "ethernet"
+ WIFI ConnectionType = "wifi"
+ CELLULAR ConnectionType = "cellular"
+ BLUETOOTH ConnectionType = "bluetooth"
+)
+
+type Connection struct {
+ Name string
+ Type ConnectionType
+ TransmitRate uint64
+ ReceiveRate uint64
+ SSID string // Wi-Fi only
}
type TemplateCache struct {
@@ -182,7 +189,7 @@ type Environment interface {
InWSLSharedDrive() bool
ConvertToLinuxPath(path string) string
ConvertToWindowsPath(path string) string
- WifiNetwork() (*WifiInfo, error)
+ Connection(connectionType ConnectionType) (*Connection, error)
TemplateCache() *TemplateCache
LoadTemplateCache()
Log(logType LogType, funcName, message string)
@@ -222,6 +229,7 @@ type ShellEnvironment struct {
fileCache *fileCache
tmplCache *TemplateCache
logBuilder strings.Builder
+ networks []*Connection
}
func (env *ShellEnvironment) Init() {
diff --git a/src/environment/shell_unix.go b/src/environment/shell_unix.go
index d0836907..7a7a68bc 100644
--- a/src/environment/shell_unix.go
+++ b/src/environment/shell_unix.go
@@ -23,7 +23,7 @@ func (env *ShellEnvironment) Home() string {
}
func (env *ShellEnvironment) QueryWindowTitles(processName, windowTitleRegex string) (string, error) {
- return "", errors.New("not implemented")
+ return "", &NotImplemented{}
}
func (env *ShellEnvironment) IsWsl() bool {
@@ -94,7 +94,7 @@ func (env *ShellEnvironment) CachePath() string {
}
func (env *ShellEnvironment) WindowsRegistryKeyValue(path string) (*WindowsRegistryValue, error) {
- return nil, errors.New("not implemented")
+ return nil, &NotImplemented{}
}
func (env *ShellEnvironment) InWSLSharedDrive() bool {
@@ -120,10 +120,6 @@ func (env *ShellEnvironment) ConvertToLinuxPath(path string) string {
return path
}
-func (env *ShellEnvironment) WifiNetwork() (*WifiInfo, error) {
- return nil, errors.New("not implemented")
-}
-
func (env *ShellEnvironment) LookWinAppPath(file string) (string, error) {
return "", errors.New("not relevant")
}
@@ -160,3 +156,11 @@ func (env *ShellEnvironment) DirIsWritable(path string) bool {
return true
}
+
+func (env *ShellEnvironment) Connection(connectionType ConnectionType) (*Connection, error) {
+ // added to disable the linting error, we can implement this later
+ if len(env.networks) == 0 {
+ return nil, &NotImplemented{}
+ }
+ return nil, &NotImplemented{}
+}
diff --git a/src/environment/shell_windows.go b/src/environment/shell_windows.go
index 6dc746b0..9afa5ff0 100644
--- a/src/environment/shell_windows.go
+++ b/src/environment/shell_windows.go
@@ -8,8 +8,6 @@ import (
"strings"
"syscall"
"time"
- "unicode/utf16"
- "unsafe"
"github.com/Azure/go-ansiterm/winterm"
"golang.org/x/sys/windows"
@@ -235,248 +233,6 @@ func (env *ShellEnvironment) ConvertToLinuxPath(path string) string {
return path
}
-var (
- hapi = syscall.NewLazyDLL("wlanapi.dll")
- hWlanOpenHandle = hapi.NewProc("WlanOpenHandle")
- hWlanCloseHandle = hapi.NewProc("WlanCloseHandle")
- hWlanEnumInterfaces = hapi.NewProc("WlanEnumInterfaces")
- hWlanQueryInterface = hapi.NewProc("WlanQueryInterface")
-)
-
-const (
- FHSS WifiType = "FHSS"
- DSSS WifiType = "DSSS"
- IR WifiType = "IR"
- A WifiType = "802.11a"
- HRDSSS WifiType = "HRDSSS"
- G WifiType = "802.11g"
- N WifiType = "802.11n"
- AC WifiType = "802.11ac"
-
- Infrastructure WifiType = "Infrastructure"
- Independent WifiType = "Independent"
- Any WifiType = "Any"
-
- OpenSystem WifiType = "802.11 Open System"
- SharedKey WifiType = "802.11 Shared Key"
- WPA WifiType = "WPA"
- WPAPSK WifiType = "WPA PSK"
- WPANone WifiType = "WPA NONE"
- WPA2 WifiType = "WPA2"
- WPA2PSK WifiType = "WPA2 PSK"
- Disabled WifiType = "disabled"
-
- None WifiType = "None"
- WEP40 WifiType = "WEP40"
- TKIP WifiType = "TKIP"
- CCMP WifiType = "CCMP"
- WEP104 WifiType = "WEP104"
- WEP WifiType = "WEP"
-)
-
-func (env *ShellEnvironment) WifiNetwork() (*WifiInfo, error) {
- env.Trace(time.Now(), "WifiNetwork")
- // Open handle
- var pdwNegotiatedVersion uint32
- var phClientHandle uint32
- e, _, err := hWlanOpenHandle.Call(uintptr(uint32(2)), uintptr(unsafe.Pointer(nil)), uintptr(unsafe.Pointer(&pdwNegotiatedVersion)), uintptr(unsafe.Pointer(&phClientHandle)))
- if e != 0 {
- return nil, err
- }
-
- // defer closing handle
- defer func() {
- _, _, _ = hWlanCloseHandle.Call(uintptr(phClientHandle), uintptr(unsafe.Pointer(nil)))
- }()
-
- // list interfaces
- var interfaceList *WLAN_INTERFACE_INFO_LIST
- e, _, err = hWlanEnumInterfaces.Call(uintptr(phClientHandle), uintptr(unsafe.Pointer(nil)), uintptr(unsafe.Pointer(&interfaceList)))
- if e != 0 {
- return nil, err
- }
-
- // use first interface that is connected
- numberOfInterfaces := int(interfaceList.dwNumberOfItems)
- infoSize := unsafe.Sizeof(interfaceList.InterfaceInfo[0])
- for i := 0; i < numberOfInterfaces; i++ {
- network := (*WLAN_INTERFACE_INFO)(unsafe.Pointer(uintptr(unsafe.Pointer(&interfaceList.InterfaceInfo[0])) + uintptr(i)*infoSize))
- if network.isState != 1 {
- continue
- }
- return env.parseNetworkInterface(network, phClientHandle)
- }
- return nil, errors.New("Not connected")
-}
-
-func (env *ShellEnvironment) parseNetworkInterface(network *WLAN_INTERFACE_INFO, clientHandle uint32) (*WifiInfo, error) {
- info := WifiInfo{}
- info.Interface = strings.TrimRight(string(utf16.Decode(network.strInterfaceDescription[:])), "\x00")
-
- // Query wifi connection state
- var dataSize uint16
- var wlanAttr *WLAN_CONNECTION_ATTRIBUTES
- e, _, err := hWlanQueryInterface.Call(uintptr(clientHandle),
- uintptr(unsafe.Pointer(&network.InterfaceGuid)),
- uintptr(7), // wlan_intf_opcode_current_connection
- uintptr(unsafe.Pointer(nil)),
- uintptr(unsafe.Pointer(&dataSize)),
- uintptr(unsafe.Pointer(&wlanAttr)),
- uintptr(unsafe.Pointer(nil)))
- if e != 0 {
- env.Log(Error, "parseNetworkInterface", "wlan_intf_opcode_current_connection error")
- return &info, err
- }
-
- // SSID
- ssid := wlanAttr.wlanAssociationAttributes.dot11Ssid
- if ssid.uSSIDLength > 0 {
- info.SSID = string(ssid.ucSSID[0:ssid.uSSIDLength])
- }
-
- // see https://docs.microsoft.com/en-us/windows/win32/nativewifi/dot11-phy-type
- switch wlanAttr.wlanAssociationAttributes.dot11PhyType {
- case 1:
- info.PhysType = FHSS
- case 2:
- info.PhysType = DSSS
- case 3:
- info.PhysType = IR
- case 4:
- info.PhysType = A
- case 5:
- info.PhysType = HRDSSS
- case 6:
- info.PhysType = G
- case 7:
- info.PhysType = N
- case 8:
- info.PhysType = AC
- default:
- info.PhysType = UNKNOWN
- }
-
- // see https://docs.microsoft.com/en-us/windows/win32/nativewifi/dot11-bss-type
- switch wlanAttr.wlanAssociationAttributes.dot11BssType {
- case 1:
- info.RadioType = Infrastructure
- case 2:
- info.RadioType = Independent
- default:
- info.RadioType = Any
- }
-
- info.Signal = int(wlanAttr.wlanAssociationAttributes.wlanSignalQuality)
- info.TransmitRate = int(wlanAttr.wlanAssociationAttributes.ulTxRate) / 1024
- info.ReceiveRate = int(wlanAttr.wlanAssociationAttributes.ulRxRate) / 1024
-
- // Query wifi channel
- dataSize = 0
- var channel *uint32
- e, _, err = hWlanQueryInterface.Call(uintptr(clientHandle),
- uintptr(unsafe.Pointer(&network.InterfaceGuid)),
- uintptr(8), // wlan_intf_opcode_channel_number
- uintptr(unsafe.Pointer(nil)),
- uintptr(unsafe.Pointer(&dataSize)),
- uintptr(unsafe.Pointer(&channel)),
- uintptr(unsafe.Pointer(nil)))
- if e != 0 {
- env.Log(Error, "parseNetworkInterface", "wlan_intf_opcode_channel_number error")
- return &info, err
- }
- info.Channel = int(*channel)
-
- if wlanAttr.wlanSecurityAttributes.bSecurityEnabled <= 0 {
- info.Authentication = Disabled
- return &info, nil
- }
-
- // see https://docs.microsoft.com/en-us/windows/win32/nativewifi/dot11-auth-algorithm
- switch wlanAttr.wlanSecurityAttributes.dot11AuthAlgorithm {
- case 1:
- info.Authentication = OpenSystem
- case 2:
- info.Authentication = SharedKey
- case 3:
- info.Authentication = WPA
- case 4:
- info.Authentication = WPAPSK
- case 5:
- info.Authentication = WPANone
- case 6:
- info.Authentication = WPA2
- case 7:
- info.Authentication = WPA2PSK
- default:
- info.Authentication = UNKNOWN
- }
-
- // see https://docs.microsoft.com/en-us/windows/win32/nativewifi/dot11-cipher-algorithm
- switch wlanAttr.wlanSecurityAttributes.dot11CipherAlgorithm {
- case 0:
- info.Cipher = None
- case 0x1:
- info.Cipher = WEP40
- case 0x2:
- info.Cipher = TKIP
- case 0x4:
- info.Cipher = CCMP
- case 0x5:
- info.Cipher = WEP104
- case 0x100:
- info.Cipher = WPA
- case 0x101:
- info.Cipher = WEP
- default:
- info.Cipher = UNKNOWN
- }
-
- return &info, nil
-}
-
-type WLAN_INTERFACE_INFO_LIST struct { //nolint: revive
- dwNumberOfItems uint32
- dwIndex uint32 //nolint: unused
- InterfaceInfo [1]WLAN_INTERFACE_INFO
-}
-
-type WLAN_INTERFACE_INFO struct { //nolint: revive
- InterfaceGuid syscall.GUID //nolint: revive
- strInterfaceDescription [256]uint16
- isState uint32
-}
-
-type WLAN_CONNECTION_ATTRIBUTES struct { //nolint: revive
- isState uint32 //nolint: unused
- wlanConnectionMode uint32 //nolint: unused
- strProfileName [256]uint16 //nolint: unused
- wlanAssociationAttributes WLAN_ASSOCIATION_ATTRIBUTES
- wlanSecurityAttributes WLAN_SECURITY_ATTRIBUTES
-}
-
-type WLAN_ASSOCIATION_ATTRIBUTES struct { //nolint: revive
- dot11Ssid DOT11_SSID
- dot11BssType uint32
- dot11Bssid [6]uint8 //nolint: unused
- dot11PhyType uint32
- uDot11PhyIndex uint32 //nolint: unused
- wlanSignalQuality uint32
- ulRxRate uint32
- ulTxRate uint32
-}
-
-type WLAN_SECURITY_ATTRIBUTES struct { //nolint: revive
- bSecurityEnabled uint32
- bOneXEnabled uint32 //nolint: unused
- dot11AuthAlgorithm uint32
- dot11CipherAlgorithm uint32
-}
-
-type DOT11_SSID struct { //nolint: revive
- uSSIDLength uint32
- ucSSID [32]uint8
-}
-
func (env *ShellEnvironment) DirIsWritable(path string) bool {
defer env.Trace(time.Now(), "DirIsWritable")
info, err := os.Stat(path)
@@ -498,3 +254,20 @@ func (env *ShellEnvironment) DirIsWritable(path string) bool {
return true
}
+
+func (env *ShellEnvironment) Connection(connectionType ConnectionType) (*Connection, error) {
+ if env.networks == nil {
+ networks := env.getConnections()
+ if len(networks) == 0 {
+ return nil, errors.New("No connections found")
+ }
+ env.networks = networks
+ }
+ for _, network := range env.networks {
+ if network.Type == connectionType {
+ return network, nil
+ }
+ }
+ env.Log(Error, "network", fmt.Sprintf("Network type '%s' not found", connectionType))
+ return nil, &NotImplemented{}
+}
diff --git a/src/environment/windows_win32.go b/src/environment/windows_win32.go
index 41acfbdf..be0c7b5c 100644
--- a/src/environment/windows_win32.go
+++ b/src/environment/windows_win32.go
@@ -24,6 +24,9 @@ var (
psapi = syscall.NewLazyDLL("psapi.dll")
getModuleBaseNameA = psapi.NewProc("GetModuleBaseNameA")
+
+ iphlpapi = syscall.NewLazyDLL("iphlpapi.dll")
+ hGetIfTable2 = iphlpapi.NewProc("GetIfTable2")
)
// enumWindows call enumWindows from user32 and returns all active windows
@@ -189,7 +192,242 @@ func readWinAppLink(path string) (string, error) {
rb := (*GenericDataBuffer)(unsafe.Pointer(&rdb.DUMMYUNIONNAME))
appExecLink := (*AppExecLinkReparseBuffer)(unsafe.Pointer(&rb.DataBuffer))
if appExecLink.Version != 3 {
- return " ", errors.New("unknown AppExecLink version")
+ return "", errors.New("unknown AppExecLink version")
}
return appExecLink.Path()
}
+
+// networks
+
+func (env *ShellEnvironment) getConnections() []*Connection {
+ var pIFTable2 *MIN_IF_TABLE2
+ _, _, _ = hGetIfTable2.Call(uintptr(unsafe.Pointer(&pIFTable2)))
+
+ SSIDs, _ := env.getAllWifiSSID()
+ networks := make([]*Connection, 0)
+
+ for i := 0; i < int(pIFTable2.NumEntries); i++ {
+ networkInterface := pIFTable2.Table[i]
+ alias := strings.TrimRight(syscall.UTF16ToString(networkInterface.Alias[:]), "\x00")
+ description := strings.TrimRight(syscall.UTF16ToString(networkInterface.Description[:]), "\x00")
+
+ if networkInterface.OperStatus != 1 || // not connected or functional
+ !networkInterface.InterfaceAndOperStatusFlags.HardwareInterface || // rule out software interfaces
+ strings.HasPrefix(alias, "Local Area Connection") || // not relevant
+ strings.Index(alias, "-") >= 3 { // rule out parts of Ethernet filter interfaces
+ // e.g. : "Ethernet-WFP Native MAC Layer LightWeight Filter-0000"
+ continue
+ }
+
+ var connectionType ConnectionType
+ switch networkInterface.Type {
+ case 6:
+ connectionType = ETHERNET
+ case 71:
+ connectionType = WIFI
+ case 237, 234, 244:
+ connectionType = CELLULAR
+ }
+
+ if networkInterface.PhysicalMediumType == 10 {
+ connectionType = BLUETOOTH
+ }
+
+ // skip connections which aren't relevant
+ if len(connectionType) == 0 {
+ continue
+ }
+
+ network := &Connection{
+ Type: connectionType,
+ Name: description, // we want a relatable name, alias isn't that
+ TransmitRate: networkInterface.TransmitLinkSpeed,
+ ReceiveRate: networkInterface.ReceiveLinkSpeed,
+ }
+
+ if SSID, OK := SSIDs[network.Name]; OK {
+ network.SSID = SSID
+ }
+
+ networks = append(networks, network)
+ }
+ return networks
+}
+
+type MIN_IF_TABLE2 struct { //nolint: revive
+ NumEntries uint64
+ Table [256]MIB_IF_ROW2
+}
+
+const (
+ IF_MAX_STRING_SIZE uint64 = 256 //nolint: revive
+ IF_MAX_PHYS_ADDRESS_LENGTH uint64 = 32 //nolint: revive
+)
+
+type MIB_IF_ROW2 struct { //nolint: revive
+ InterfaceLuid uint64
+ InterfaceIndex uint32
+ InterfaceGUID windows.GUID
+ Alias [IF_MAX_STRING_SIZE + 1]uint16
+ Description [IF_MAX_STRING_SIZE + 1]uint16
+ PhysicalAddressLength uint32
+ PhysicalAddress [IF_MAX_PHYS_ADDRESS_LENGTH]uint8
+ PermanentPhysicalAddress [IF_MAX_PHYS_ADDRESS_LENGTH]uint8
+
+ Mtu uint32
+ Type uint32
+ TunnelType uint32
+ MediaType uint32
+ PhysicalMediumType uint32
+ AccessType uint32
+ DirectionType uint32
+
+ InterfaceAndOperStatusFlags struct {
+ HardwareInterface bool
+ FilterInterface bool
+ ConnectorPresent bool
+ NotAuthenticated bool
+ NotMediaConnected bool
+ Paused bool
+ LowPower bool
+ EndPointInterface bool
+ }
+
+ OperStatus uint32
+ AdminStatus uint32
+ MediaConnectState uint32
+ NetworkGUID windows.GUID
+ ConnectionType uint32
+
+ TransmitLinkSpeed uint64
+ ReceiveLinkSpeed uint64
+
+ InOctets uint64
+ InUcastPkts uint64
+ InNUcastPkts uint64
+ InDiscards uint64
+ InErrors uint64
+ InUnknownProtos uint64
+ InUcastOctets uint64
+ InMulticastOctets uint64
+ InBroadcastOctets uint64
+ OutOctets uint64
+ OutUcastPkts uint64
+ OutNUcastPkts uint64
+ OutDiscards uint64
+ OutErrors uint64
+ OutUcastOctets uint64
+ OutMulticastOctets uint64
+ OutBroadcastOctets uint64
+ OutQLen uint64
+}
+
+func (env *ShellEnvironment) getAllWifiSSID() (map[string]string, error) {
+ var pdwNegotiatedVersion uint32
+ var phClientHandle uint32
+ e, _, err := hWlanOpenHandle.Call(uintptr(uint32(2)), uintptr(unsafe.Pointer(nil)), uintptr(unsafe.Pointer(&pdwNegotiatedVersion)), uintptr(unsafe.Pointer(&phClientHandle)))
+ if e != 0 {
+ env.Log(Error, "getAllWifiSSID", err.Error())
+ return nil, err
+ }
+
+ // defer closing handle
+ defer func() {
+ _, _, _ = hWlanCloseHandle.Call(uintptr(phClientHandle), uintptr(unsafe.Pointer(nil)))
+ }()
+
+ ssid := make(map[string]string)
+ // list interfaces
+ var interfaceList *WLAN_INTERFACE_INFO_LIST
+ e, _, err = hWlanEnumInterfaces.Call(uintptr(phClientHandle), uintptr(unsafe.Pointer(nil)), uintptr(unsafe.Pointer(&interfaceList)))
+ if e != 0 {
+ env.Log(Error, "getAllWifiSSID", err.Error())
+ return nil, err
+ }
+
+ // use first interface that is connected
+ numberOfInterfaces := int(interfaceList.dwNumberOfItems)
+ infoSize := unsafe.Sizeof(interfaceList.InterfaceInfo[0])
+ for i := 0; i < numberOfInterfaces; i++ {
+ network := (*WLAN_INTERFACE_INFO)(unsafe.Pointer(uintptr(unsafe.Pointer(&interfaceList.InterfaceInfo[0])) + uintptr(i)*infoSize))
+ if network.isState == 1 {
+ wifiInterface := strings.TrimRight(string(utf16.Decode(network.strInterfaceDescription[:])), "\x00")
+ ssid[wifiInterface] = env.getWiFiSSID(network, phClientHandle)
+ }
+ }
+ return ssid, nil
+}
+
+var (
+ wlanapi = syscall.NewLazyDLL("wlanapi.dll")
+ hWlanOpenHandle = wlanapi.NewProc("WlanOpenHandle")
+ hWlanCloseHandle = wlanapi.NewProc("WlanCloseHandle")
+ hWlanEnumInterfaces = wlanapi.NewProc("WlanEnumInterfaces")
+ hWlanQueryInterface = wlanapi.NewProc("WlanQueryInterface")
+)
+
+func (env *ShellEnvironment) getWiFiSSID(network *WLAN_INTERFACE_INFO, clientHandle uint32) string {
+ // Query wifi connection state
+ var dataSize uint16
+ var wlanAttr *WLAN_CONNECTION_ATTRIBUTES
+ e, _, _ := hWlanQueryInterface.Call(uintptr(clientHandle),
+ uintptr(unsafe.Pointer(&network.InterfaceGuid)),
+ uintptr(7), // wlan_intf_opcode_current_connection
+ uintptr(unsafe.Pointer(nil)),
+ uintptr(unsafe.Pointer(&dataSize)),
+ uintptr(unsafe.Pointer(&wlanAttr)),
+ uintptr(unsafe.Pointer(nil)))
+ if e != 0 {
+ env.Log(Error, "parseWlanInterface", "wlan_intf_opcode_current_connection error")
+ return ""
+ }
+
+ ssid := wlanAttr.wlanAssociationAttributes.dot11Ssid
+ if ssid.uSSIDLength <= 0 {
+ return ""
+ }
+ return string(ssid.ucSSID[0:ssid.uSSIDLength])
+}
+
+type WLAN_INTERFACE_INFO_LIST struct { //nolint: revive
+ dwNumberOfItems uint32
+ dwIndex uint32 //nolint: unused
+ InterfaceInfo [256]WLAN_INTERFACE_INFO
+}
+
+type WLAN_INTERFACE_INFO struct { //nolint: revive
+ InterfaceGuid syscall.GUID //nolint: revive
+ strInterfaceDescription [256]uint16
+ isState uint32
+}
+
+type WLAN_CONNECTION_ATTRIBUTES struct { //nolint: revive
+ isState uint32 //nolint: unused
+ wlanConnectionMode uint32 //nolint: unused
+ strProfileName [256]uint16 //nolint: unused
+ wlanAssociationAttributes WLAN_ASSOCIATION_ATTRIBUTES
+ wlanSecurityAttributes WLAN_SECURITY_ATTRIBUTES //nolint: unused
+}
+
+type WLAN_ASSOCIATION_ATTRIBUTES struct { //nolint: revive
+ dot11Ssid DOT11_SSID
+ dot11BssType uint32 //nolint: unused
+ dot11Bssid [6]uint8 //nolint: unused
+ dot11PhyType uint32 //nolint: unused
+ uDot11PhyIndex uint32 //nolint: unused
+ wlanSignalQuality uint32 //nolint: unused
+ ulRxRate uint32 //nolint: unused
+ ulTxRate uint32 //nolint: unused
+}
+
+type WLAN_SECURITY_ATTRIBUTES struct { //nolint: revive
+ bSecurityEnabled uint32 //nolint: unused
+ bOneXEnabled uint32 //nolint: unused
+ dot11AuthAlgorithm uint32 //nolint: unused
+ dot11CipherAlgorithm uint32 //nolint: unused
+}
+
+type DOT11_SSID struct { //nolint: revive
+ uSSIDLength uint32
+ ucSSID [32]uint8
+}
diff --git a/src/go.mod b/src/go.mod
index 34387476..6956a20c 100644
--- a/src/go.mod
+++ b/src/go.mod
@@ -38,7 +38,7 @@ require (
github.com/hashicorp/hcl/v2 v2.14.0
github.com/mattn/go-runewidth v0.0.13
github.com/spf13/cobra v1.5.0
- golang.org/x/mod v0.5.1
+ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
gopkg.in/yaml.v3 v3.0.1
)
@@ -60,7 +60,6 @@ require (
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.5.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
- golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
)
require (
diff --git a/src/go.sum b/src/go.sum
index 35653704..c38d49e6 100644
--- a/src/go.sum
+++ b/src/go.sum
@@ -222,8 +222,8 @@ golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
-golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
-golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
@@ -265,8 +265,6 @@ golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
-golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/src/mock/environment.go b/src/mock/environment.go
index 344ca870..41f1cfe3 100644
--- a/src/mock/environment.go
+++ b/src/mock/environment.go
@@ -208,9 +208,9 @@ func (env *MockedEnvironment) ConvertToLinuxPath(path string) string {
return args.String(0)
}
-func (env *MockedEnvironment) WifiNetwork() (*environment.WifiInfo, error) {
- args := env.Called()
- return args.Get(0).(*environment.WifiInfo), args.Error(1)
+func (env *MockedEnvironment) Connection(connectionType environment.ConnectionType) (*environment.Connection, error) {
+ args := env.Called(connectionType)
+ return args.Get(0).(*environment.Connection), args.Error(1)
}
func (env *MockedEnvironment) TemplateCache() *environment.TemplateCache {
diff --git a/src/properties/properties.go b/src/properties/properties.go
index f79d6fe8..deb25f93 100644
--- a/src/properties/properties.go
+++ b/src/properties/properties.go
@@ -60,15 +60,7 @@ func (m Map) GetString(property Property, defaultValue string) string {
if !found {
return defaultValue
}
- return ParseString(val, defaultValue)
-}
-
-func ParseString(value interface{}, defaultValue string) string {
- stringValue, ok := value.(string)
- if !ok {
- return defaultValue
- }
- return stringValue
+ return fmt.Sprint(val)
}
func (m Map) GetColor(property Property, defaultValue string) string {
@@ -76,7 +68,7 @@ func (m Map) GetColor(property Property, defaultValue string) string {
if !found {
return defaultValue
}
- colorString := ParseString(val, defaultValue)
+ colorString := fmt.Sprint(val)
if color.IsAnsiColorName(colorString) {
return colorString
}
diff --git a/src/properties/properties_test.go b/src/properties/properties_test.go
index ced71751..409423c9 100644
--- a/src/properties/properties_test.go
+++ b/src/properties/properties_test.go
@@ -28,7 +28,7 @@ func TestGetStringNoEntry(t *testing.T) {
func TestGetStringNoTextEntry(t *testing.T) {
var properties = Map{Foo: true}
value := properties.GetString(Foo, expected)
- assert.Equal(t, expected, value)
+ assert.Equal(t, "true", value)
}
func TestGetHexColor(t *testing.T) {
diff --git a/src/segments/connection.go b/src/segments/connection.go
new file mode 100644
index 00000000..4891b1f3
--- /dev/null
+++ b/src/segments/connection.go
@@ -0,0 +1,41 @@
+package segments
+
+import (
+ "oh-my-posh/environment"
+ "oh-my-posh/properties"
+ "strings"
+)
+
+type Connection struct {
+ props properties.Properties
+ env environment.Environment
+
+ environment.Connection
+}
+
+const (
+ Type properties.Property = "type"
+)
+
+func (c *Connection) Template() string {
+ return " {{ if eq .Type \"wifi\"}}\uf1eb{{ else if eq .Type \"ethernet\"}}\uf6ff{{ end }} "
+}
+
+func (c *Connection) Enabled() bool {
+ types := c.props.GetString(Type, "wifi|ethernet")
+ connectionTypes := strings.Split(types, "|")
+ for _, connectionType := range connectionTypes {
+ network, err := c.env.Connection(environment.ConnectionType(connectionType))
+ if err != nil {
+ continue
+ }
+ c.Connection = *network
+ return true
+ }
+ return false
+}
+
+func (c *Connection) Init(props properties.Properties, env environment.Environment) {
+ c.props = props
+ c.env = env
+}
diff --git a/src/segments/connection_test.go b/src/segments/connection_test.go
new file mode 100644
index 00000000..e365fc5a
--- /dev/null
+++ b/src/segments/connection_test.go
@@ -0,0 +1,105 @@
+package segments
+
+import (
+ "fmt"
+ "oh-my-posh/environment"
+ "oh-my-posh/mock"
+ "oh-my-posh/properties"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestConnection(t *testing.T) {
+ type connectionResponse struct {
+ Connection *environment.Connection
+ Error error
+ }
+ cases := []struct {
+ Case string
+ ExpectedString string
+ ExpectedEnabled bool
+ ConnectionType string
+ Connections []*connectionResponse
+ }{
+ {
+ Case: "WiFi only, enabled",
+ ExpectedString: "\uf1eb",
+ ExpectedEnabled: true,
+ ConnectionType: "wifi",
+ Connections: []*connectionResponse{
+ {
+ Connection: &environment.Connection{
+ Name: "WiFi",
+ Type: "wifi",
+ },
+ },
+ },
+ },
+ {
+ Case: "WiFi only, disabled",
+ ConnectionType: "wifi",
+ Connections: []*connectionResponse{
+ {
+ Connection: &environment.Connection{
+ Type: environment.WIFI,
+ },
+ Error: fmt.Errorf("no connection"),
+ },
+ },
+ },
+ {
+ Case: "WiFi and Ethernet, enabled",
+ ConnectionType: "wifi|ethernet",
+ ExpectedString: "\uf6ff",
+ ExpectedEnabled: true,
+ Connections: []*connectionResponse{
+ {
+ Connection: &environment.Connection{
+ Type: environment.WIFI,
+ },
+ Error: fmt.Errorf("no connection"),
+ },
+ {
+ Connection: &environment.Connection{
+ Type: environment.ETHERNET,
+ },
+ },
+ },
+ },
+ {
+ Case: "WiFi and Ethernet, disabled",
+ ConnectionType: "wifi|ethernet",
+ Connections: []*connectionResponse{
+ {
+ Connection: &environment.Connection{
+ Type: environment.WIFI,
+ },
+ Error: fmt.Errorf("no connection"),
+ },
+ {
+ Connection: &environment.Connection{
+ Type: environment.ETHERNET,
+ },
+ Error: fmt.Errorf("no connection"),
+ },
+ },
+ },
+ }
+ for _, tc := range cases {
+ env := &mock.MockedEnvironment{}
+ for _, con := range tc.Connections {
+ env.On("Connection", con.Connection.Type).Return(con.Connection, con.Error)
+ }
+ c := &Connection{
+ env: env,
+ props: &properties.Map{
+ Type: tc.ConnectionType,
+ },
+ }
+ assert.Equal(t, tc.ExpectedEnabled, c.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case))
+ if tc.ExpectedEnabled {
+ assert.Equal(t, tc.ExpectedString, renderTemplate(env, c.Template(), c), fmt.Sprintf("Failed in case: %s", tc.Case))
+ }
+ }
+}
diff --git a/src/segments/spotify_windows_test.go b/src/segments/spotify_windows_test.go
index 670b0f25..a100d766 100644
--- a/src/segments/spotify_windows_test.go
+++ b/src/segments/spotify_windows_test.go
@@ -3,7 +3,7 @@
package segments
import (
- "errors"
+ "oh-my-posh/environment"
"oh-my-posh/mock"
"oh-my-posh/properties"
"testing"
@@ -40,7 +40,7 @@ func TestSpotifyWindowsNative(t *testing.T) {
for _, tc := range cases {
env := new(mock.MockedEnvironment)
env.On("QueryWindowTitles", "spotify.exe", `^(Spotify.*)|(.*\s-\s.*)$`).Return(tc.Title, tc.Error)
- env.On("QueryWindowTitles", "msedge.exe", `^(Spotify.*)`).Return("", errors.New("not implemented"))
+ env.On("QueryWindowTitles", "msedge.exe", `^(Spotify.*)`).Return("", &environment.NotImplemented{})
s := &Spotify{
env: env,
props: properties.Map{},
@@ -74,7 +74,7 @@ func TestSpotifyWindowsPWA(t *testing.T) {
}
for _, tc := range cases {
env := new(mock.MockedEnvironment)
- env.On("QueryWindowTitles", "spotify.exe", "^(Spotify.*)|(.*\\s-\\s.*)$").Return("", errors.New("not implemented"))
+ env.On("QueryWindowTitles", "spotify.exe", "^(Spotify.*)|(.*\\s-\\s.*)$").Return("", &environment.NotImplemented{})
env.On("QueryWindowTitles", "msedge.exe", "^(Spotify.*)").Return(tc.Title, tc.Error)
s := &Spotify{
env: env,
diff --git a/src/segments/wifi.go b/src/segments/wifi.go
deleted file mode 100644
index 284508d8..00000000
--- a/src/segments/wifi.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package segments
-
-import (
- "oh-my-posh/environment"
- "oh-my-posh/properties"
-)
-
-type Wifi struct {
- props properties.Properties
- env environment.Environment
-
- Error string
-
- environment.WifiInfo
-}
-
-const (
- defaultTemplate = " {{ if .Error }}{{ .Error }}{{ else }}\uFAA8 {{ .SSID }} {{ .Signal }}% {{ .ReceiveRate }}Mbps{{ end }} "
-)
-
-func (w *Wifi) Template() string {
- return defaultTemplate
-}
-
-func (w *Wifi) Enabled() bool {
- // This segment only supports Windows/WSL for now
- if w.env.Platform() != environment.WINDOWS && !w.env.IsWsl() {
- return false
- }
- wifiInfo, err := w.env.WifiNetwork()
- displayError := w.props.GetBool(properties.DisplayError, false)
- if err != nil && displayError {
- w.Error = err.Error()
- return true
- }
- if err != nil || wifiInfo == nil {
- return false
- }
- w.WifiInfo = *wifiInfo
- return true
-}
-
-func (w *Wifi) Init(props properties.Properties, env environment.Environment) {
- w.props = props
- w.env = env
-}
diff --git a/src/segments/wifi_test.go b/src/segments/wifi_test.go
deleted file mode 100644
index 0aeb353b..00000000
--- a/src/segments/wifi_test.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package segments
-
-import (
- "errors"
- "oh-my-posh/environment"
- "oh-my-posh/mock"
- "oh-my-posh/properties"
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestWiFiSegment(t *testing.T) {
- cases := []struct {
- Case string
- ExpectedString string
- ExpectedEnabled bool
- Network *environment.WifiInfo
- WifiError error
- DisplayError bool
- }{
- {
- Case: "No error and nil network",
- },
- {
- Case: "Error and nil network",
- WifiError: errors.New("oh noes"),
- },
- {
- Case: "Display error and nil network",
- WifiError: errors.New("oh noes"),
- ExpectedString: "oh noes",
- DisplayError: true,
- ExpectedEnabled: true,
- },
- {
- Case: "Display wifi state",
- ExpectedString: "pretty fly for a wifi",
- ExpectedEnabled: true,
- Network: &environment.WifiInfo{
- SSID: "pretty fly for a wifi",
- },
- },
- }
-
- for _, tc := range cases {
- env := new(mock.MockedEnvironment)
- env.On("Platform").Return(environment.WINDOWS)
- env.On("IsWsl").Return(false)
- env.On("WifiNetwork").Return(tc.Network, tc.WifiError)
-
- w := &Wifi{
- env: env,
- props: properties.Map{
- properties.DisplayError: tc.DisplayError,
- },
- }
-
- assert.Equal(t, tc.ExpectedEnabled, w.Enabled(), tc.Case)
- if tc.Network != nil || tc.DisplayError {
- assert.Equal(t, tc.ExpectedString, renderTemplate(env, "{{ if .Error }}{{ .Error }}{{ else }}{{ .SSID }}{{ end }}", w), tc.Case)
- }
- }
-}
diff --git a/themes/schema.json b/themes/schema.json
index aad434bd..4ba053cf 100644
--- a/themes/schema.json
+++ b/themes/schema.json
@@ -225,6 +225,7 @@
"angular",
"battery",
"command",
+ "connection",
"crystal",
"cds",
"cf",
@@ -274,7 +275,6 @@
"terraform",
"ui5tooling",
"wakatime",
- "wifi",
"winreg",
"withings",
"ytm"
@@ -575,6 +575,55 @@
}
}
},
+ {
+ "if": {
+ "properties": {
+ "type": {
+ "const": "connection"
+ }
+ }
+ },
+ "then": {
+ "title": "Connection Segment",
+ "description": "https://ohmyposh.dev/docs/segments/connection",
+ "properties": {
+ "properties": {
+ "properties": {
+ "type": {
+ "type": "string",
+ "title": "Connection type",
+ "description": "The connection type to display",
+ "enum": [
+ "ethernet",
+ "wifi",
+ "cellular",
+ "bluetooth"
+ ],
+ "default": "wifi|ethernet"
+ },
+ "unit": {
+ "type": "string",
+ "title": "Transfer speed unit",
+ "enum": [
+ "none",
+ "b",
+ "bps",
+ "K",
+ "Kbps",
+ "M",
+ "Mbps",
+ "G",
+ "Gbps",
+ "T",
+ "Tbps"
+ ],
+ "default": "none"
+ }
+ }
+ }
+ }
+ }
+ },
{
"if": {
"properties": {
@@ -2164,19 +2213,6 @@
}
}
},
- {
- "if": {
- "properties": {
- "type": {
- "const": "wifi"
- }
- }
- },
- "then": {
- "title": "WiFi Segment",
- "description": "https://ohmyposh.dev/docs/segments/wifi"
- }
- },
{
"if": {
"properties": {
diff --git a/website/docs/contributing/segment.mdx b/website/docs/contributing/segment.mdx
index 3a82431f..eeb02928 100644
--- a/website/docs/contributing/segment.mdx
+++ b/website/docs/contributing/segment.mdx
@@ -85,7 +85,7 @@ go build -o $GOPATH/bin/oh-my-posh
## Add the documentation
-Create a new `markdown` file underneath the [`docs/docs/segments`][docs] folder called `new.md`.
+Create a new `markdown` file underneath the [`website/docs/segments`][docs] folder called `new.md`.
Use the following template as a guide.
````markdown
diff --git a/website/docs/segments/connection.mdx b/website/docs/segments/connection.mdx
new file mode 100644
index 00000000..291a6a32
--- /dev/null
+++ b/website/docs/segments/connection.mdx
@@ -0,0 +1,51 @@
+---
+id: connection
+title: Connection
+sidebar_label: Connection
+---
+
+## Connection
+
+Show details about the currently connected network.
+
+:::info
+Currently only supports Windows. Pull requests for Darwin and Linux support are welcome :)
+:::
+
+## Sample Configuration
+
+```json
+{
+ "type": "connection",
+ "style": "powerline",
+ "background": "#8822ee",
+ "foreground": "#222222",
+ "powerline_symbol": "\uE0B0"
+}
+```
+
+## Properties
+
+| Name | Type | Description |
+| ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `type` | `string` | the type of connection to display. Can be a single value or multiple joined by