Files
Razer-led-mac/utils/menu.go
2026-02-09 04:07:32 +08:00

321 lines
11 KiB
Go

package utils
import (
"fmt"
"github.com/progrium/darwinkit/macos/appkit"
"github.com/progrium/darwinkit/objc"
)
var Devs map[string]*RazerDevice = make(map[string]*RazerDevice)
var Temp = TempData{}
type TempData struct {
TempKey string
TempState State
}
var DeviceStateCache = make(map[string]State)
type State interface {
GetEffect() int
GetColorKey() string
GetMultiColorKey() string
GetModel() int
SetColor([]byte)
GetColor() []byte
GetColor1() []byte
GetColor2() []byte
GetSpeed() int
}
// StaticState 常亮模式
type StaticState struct {
Effect int
ColorKey string
Color []byte
}
func (s *StaticState) GetEffect() int {
return s.Effect
}
func (s *StaticState) GetModel() int {
return 0
}
func (s *StaticState) GetColorKey() string {
return s.ColorKey
}
func (s *StaticState) GetMultiColorKey() string {
return ""
}
func (s *StaticState) SetColor(color []byte) {
s.Color = color
}
func (s *StaticState) GetColor() []byte {
return s.Color
}
func (s *StaticState) GetColor1() []byte {
return []byte{}
}
func (s *StaticState) GetColor2() []byte {
return []byte{}
}
func (s *StaticState) GetSpeed() int {
return 0
}
// BreathingState 呼吸模式
type BreathingState struct {
Effect int
Model int // 单色呼吸 双色呼吸 随机颜色
Speed int // 快 中 慢
Color []byte
ColorKey string
Color1 []byte // 多色
Color2 []byte // 多色
MultiColorKey string // 多色
}
func (b *BreathingState) GetEffect() int {
return b.Effect
}
func (b *BreathingState) GetColorKey() string {
return b.ColorKey
}
func (b *BreathingState) GetModel() int {
return b.Model
}
func (b *BreathingState) GetMultiColorKey() string {
return b.MultiColorKey
}
func (b *BreathingState) SetColor(color []byte) {
b.Color = color
}
func (b *BreathingState) GetColor() []byte {
return b.Color
}
func (b *BreathingState) GetSpeed() int {
return b.Speed
}
func (b *BreathingState) GetColor1() []byte {
return b.Color1
}
func (b *BreathingState) GetColor2() []byte {
return b.Color2
}
func UpdateMenu(menu appkit.Menu, app appkit.Application, statusBarItem appkit.StatusItem) {
menu.RemoveAllItems()
// 1. 刷新按钮
refreshItem := appkit.NewMenuItemWithAction("🔄 刷新设备列表", "r", func(sender objc.Object) {
UpdateMenu(menu, app, statusBarItem)
})
menu.AddItem(refreshItem)
menu.AddItem(appkit.MenuItem_SeparatorItem())
devices := ScanDevices()
Devs = devices
// 更新 ToolTip 显示当前连接数
statusBarItem.Button().SetToolTip(fmt.Sprintf("雷蛇控制器 - 已连接设备: %d", len(devices)))
if len(devices) == 0 {
none := appkit.NewMenuItemWithAction("未发现雷蛇设备", "", func(sender objc.Object) {})
none.SetEnabled(false)
menu.AddItem(none)
} else {
for _, dev := range devices {
key := fmt.Sprintf("%s|%s|%d", dev.Serial, dev.Product, dev.ProductID)
stateVal := DeviceStateCache[key]
// 设备标题 (不可点击)
devTitle := appkit.NewMenuItemWithAction("⌨️ "+dev.Name, "", func(sender objc.Object) {})
devTitle.SetEnabled(false)
menu.AddItem(devTitle)
// 切换灯效模式 (二级菜单)
//modeMenuItem := appkit.NewMenuItemWithAction("⚙️ 切换灯效模式", "", func(sender objc.Object) {})
//modeSubMenu := appkit.NewMenuWithTitle("Select Mode")
// --- A. 常亮模式 ---
staticItem := appkit.NewMenuItemWithAction("☀️ 常亮模式", "", func(sender objc.Object) {})
if dev.CurrentEffect == KBD_EFFECT_STATIC {
staticItem.SetState(appkit.ControlStateValueOn)
}
colorMenu := appkit.NewMenuWithTitle("Colors")
for _, c := range PRESET_COLORS {
colorItem := appkit.NewMenuItemWithAction(c.Name, "", func(sender objc.Object) {
if c.Name == "自定义" {
DeviceStateCache[key] = &StaticState{Effect: KBD_EFFECT_STATIC, ColorKey: c.Name}
Temp = TempData{TempKey: key, TempState: DeviceStateCache[key]} // [key] = StaticState{Effect: KBD_EFFECT_STATIC, ColorKey: c.Name}
openColorPicker()
} else {
//dev.ApplyEffect(KBD_EFFECT_STATIC, c.R, c.G, c.B)
dev.SetStaticColor([]byte{c.R, c.G, c.B})
// 更新状态与缓存
DeviceStateCache[key] = &StaticState{Effect: KBD_EFFECT_STATIC, ColorKey: c.Name, Color: []byte{c.R, c.G, c.B}}
}
UpdateMenu(menu, app, statusBarItem)
})
stateVal := DeviceStateCache[key]
if stateVal != nil && dev.CurrentEffect == KBD_EFFECT_STATIC && stateVal.GetColorKey() == c.Name {
colorItem.SetState(appkit.ControlStateValueOn)
}
colorMenu.AddItem(colorItem)
}
staticItem.SetSubmenu(colorMenu)
menu.AddItem(staticItem)
// --- B. 呼吸模式 ---
breathItem := appkit.NewMenuItemWithAction("🌬️ 呼吸模式", "", func(sender objc.Object) {})
if dev.CurrentEffect == KBD_EFFECT_BREATHING {
breathItem.SetState(appkit.ControlStateValueOn)
}
breathRootMenu := appkit.NewMenuWithTitle("BreathingRoot")
singleItem := appkit.NewMenuItemWithAction("单色模式", "", func(sender objc.Object) {})
singleSub := appkit.NewMenuWithTitle("Single")
for _, color := range PRESET_COLORS {
item := appkit.NewMenuItemWithAction(color.Name, "", func(sender objc.Object) {
if color.Name == "自定义" {
DeviceStateCache[key] = &StaticState{Effect: KBD_EFFECT_BREATHING, ColorKey: color.Name}
Temp = TempData{TempKey: key, TempState: DeviceStateCache[key]} // [key] = StaticState{Effect: KBD_EFFECT_STATIC, ColorKey: c.Name}
openColorPicker()
} else {
if stateVal != nil {
dev.SetBreathingColor(0x01, []byte{color.R, color.G, color.B}, []byte{}, byte(stateVal.GetSpeed()))
} else {
dev.SetBreathingColor(0x01, []byte{color.R, color.G, color.B}, []byte{}, byte(1))
}
}
//dev.SetBreathing(0x01, color.R, color.G, color.B, 0, 0, 0, curState.Speed)
// 0x01: 单色 (只看第一组 RGB)
// 0x02: 双色 (看两组 RGB)
// 0x03: 随机
DeviceStateCache[key] = &BreathingState{Effect: KBD_EFFECT_BREATHING, Model: 0x01, ColorKey: color.Name, Color: []byte{color.R, color.G, color.B}}
UpdateMenu(menu, app, statusBarItem)
})
stateVal := DeviceStateCache[key]
if dev.CurrentEffect == KBD_EFFECT_BREATHING && stateVal != nil && stateVal.GetModel() == 0x01 && stateVal.GetColorKey() == color.Name {
item.SetState(appkit.ControlStateValueOn)
}
singleSub.AddItem(item)
}
stateVal = DeviceStateCache[key]
if dev.CurrentEffect == KBD_EFFECT_BREATHING && stateVal != nil && stateVal.GetModel() == 0x01 {
singleItem.SetState(appkit.ControlStateValueOn)
}
singleItem.SetSubmenu(singleSub)
breathRootMenu.AddItem(singleItem)
// B2. 双色呼吸 (红 <-> 自选)
dualItem := appkit.NewMenuItemWithAction("双色模式", "", func(sender objc.Object) {})
dualSub := appkit.NewMenuWithTitle("Dual")
for _, multiColor := range PRESET_MULTI_COLORS {
item := appkit.NewMenuItemWithAction(multiColor.Name, "", func(sender objc.Object) {
if stateVal != nil {
dev.SetBreathingColor(0x02, multiColor.Color1, multiColor.Color2, byte(stateVal.GetSpeed()))
} else {
dev.SetBreathingColor(0x02, multiColor.Color1, multiColor.Color2, byte(1))
}
DeviceStateCache[key] = &BreathingState{Effect: KBD_EFFECT_BREATHING, Model: 0x02, MultiColorKey: multiColor.Name, Color1: multiColor.Color1, Color2: multiColor.Color2}
UpdateMenu(menu, app, statusBarItem)
})
if dev.CurrentEffect == KBD_EFFECT_BREATHING && stateVal != nil && stateVal.GetModel() == 0x02 && stateVal.GetMultiColorKey() == multiColor.Name {
item.SetState(appkit.ControlStateValueOn)
}
dualSub.AddItem(item)
}
stateVal = DeviceStateCache[key]
if dev.CurrentEffect == KBD_EFFECT_BREATHING && stateVal != nil && stateVal.GetModel() == 0x02 {
dualItem.SetState(appkit.ControlStateValueOn)
}
dualItem.SetSubmenu(dualSub)
breathRootMenu.AddItem(dualItem)
// B3. 随机
randItem := appkit.NewMenuItemWithAction("随机颜色", "", func(sender objc.Object) {
DeviceStateCache[key] = &BreathingState{Effect: KBD_EFFECT_BREATHING, Model: 0x03}
UpdateMenu(menu, app, statusBarItem)
})
stateVal = DeviceStateCache[key]
if dev.CurrentEffect == KBD_EFFECT_BREATHING && stateVal != nil && stateVal.GetModel() == 0x03 {
randItem.SetState(appkit.ControlStateValueOn)
}
breathRootMenu.AddItem(randItem)
breathRootMenu.AddItem(appkit.MenuItem_SeparatorItem())
breathItem.SetSubmenu(breathRootMenu)
menu.AddItem(breathItem)
//// --- C. 波浪模式 ---
//waveItem := appkit.NewMenuItemWithAction("🌊 波浪模式", "", func(sender objc.Object) {
// //dev.ApplyEffect(KBD_EFFECT_WAVE, 0x01, 0x00, 0x00)
// key := fmt.Sprintf("%s|%s|%d", dev.Serial, dev.Product, dev.ProductID)
// deviceStateCache[key] = State{Effect: KBD_EFFECT_WAVE, Color: ""}
// UpdateMenu(menu, app, statusBarItem)
//})
//if dev.CurrentEffect == KBD_EFFECT_WAVE {
// waveItem.SetState(appkit.ControlStateValueOn)
//}
//menu.AddItem(waveItem)
// --- D. 响应模式 ---
//reactiveItem := appkit.NewMenuItemWithAction("🌊 响应模式", "", func(sender objc.Object) {
// //dev.ApplyEffect(KBD_EFFECT_WAVE, 0x01, 0x00, 0x00)
// key := fmt.Sprintf("%s|%s|%d", dev.Serial, dev.Product, dev.ProductID)
// deviceStateCache[key] = State{Effect: KBD_EFFECT_REACTIVE, Color: ""}
// UpdateMenu(menu, app, statusBarItem)
//})
//if dev.CurrentEffect == KBD_EFFECT_REACTIVE {
// reactiveItem.SetState(appkit.ControlStateValueOn)
//}
//menu.AddItem(reactiveItem)
// --- E. 关灯模式 --- 关灯
//offLightsItem := appkit.NewMenuItemWithAction("关灯模式", "", func(sender objc.Object) {
// dev.SetOffLightsColor([]byte{0x00, 0x00, 0x00})
// //key := fmt.Sprintf("%s|%s|%d", dev.Serial, dev.Product, dev.ProductID)
// deviceStateCache[key] = State{Effect: KBD_EFFECT_OFFLIGHTS, Color: ""}
// UpdateMenu(menu, app, statusBarItem)
//})
//if dev.CurrentEffect == KBD_EFFECT_OFFLIGHTS {
// offLightsItem.SetState(appkit.ControlStateValueOn)
//}
//menu.AddItem(offLightsItem)
//modeMenuItem.SetSubmenu(modeSubMenu)
//menu.AddItem(modeMenuItem)
menu.AddItem(appkit.MenuItem_SeparatorItem())
}
}
menu.AddItem(appkit.NewMenuItemWithAction("退出", "q", func(sender objc.Object) {
app.Terminate(nil)
}))
}
func SetupStatusBar(app appkit.Application) {
// 创建状态栏项
item := appkit.StatusBar_SystemStatusBar().StatusItemWithLength(appkit.VariableStatusItemLength)
objc.Retain(&item)
// 设置图标
img := appkit.Image_ImageWithSystemSymbolNameAccessibilityDescription("antenna.radiowaves.left.and.right", "Razer Tool")
item.Button().SetImage(img)
// 设置描述
item.Button().SetToolTip("雷蛇设备控制器 (Razer Controller)")
// 创建初始菜单
menu := appkit.NewMenuWithTitle("Razer")
UpdateMenu(menu, app, item)
item.SetMenu(menu)
}