返回 DeepSeek-Reasonix
systray_darwin.go
根目录 / desktop / third_party / systray / systray_darwin.go
1 //go:build !ios
2
3 package systray
4
5 /*
6 #cgo darwin CFLAGS: -DDARWIN -x objective-c -fobjc-arc
7 #cgo darwin LDFLAGS: -framework Cocoa
8
9 #include <stdbool.h>
10 #include "systray.h"
11
12 void setInternalLoop(bool);
13 */
14 import "C"
15
16 import (
17 "fmt"
18 "log"
19 "os"
20 "strings"
21 "unsafe"
22 )
23
24 // darwinKeyEquivalents maps the platform neutral key names used by SetShortcut to the
25 // key equivalent characters that AppKit expects.
26 var darwinKeyEquivalents = map[string]string{
27 "BackSpace": "\x08",
28 "Delete": "\x7f",
29 "Down": "\uf701",
30 "End": "\uf72b",
31 "Enter": "\x03",
32 "Escape": "\x1b",
33 "F1": "\uf704",
34 "F2": "\uf705",
35 "F3": "\uf706",
36 "F4": "\uf707",
37 "F5": "\uf708",
38 "F6": "\uf709",
39 "F7": "\uf70a",
40 "F8": "\uf70b",
41 "F9": "\uf70c",
42 "F10": "\uf70d",
43 "F11": "\uf70e",
44 "F12": "\uf70f",
45 "Home": "\uf729",
46 "Insert": "\uf727",
47 "Left": "\uf702",
48 "PageDown": "\uf72d",
49 "PageUp": "\uf72c",
50 "Return": "\n",
51 "Right": "\uf703",
52 "Space": " ",
53 "Tab": "\t",
54 "Up": "\uf700",
55 }
56
57 // keyEquivalent returns the AppKit key equivalent for the shortcut of this item,
58 // which is empty if no shortcut is set.
59 func (item *MenuItem) keyEquivalent() string {
60 if item.shortcutKey == "" {
61 return ""
62 }
63
64 if key, ok := darwinKeyEquivalents[item.shortcutKey]; ok {
65 return key
66 }
67 if len(item.shortcutKey) > 1 {
68 log.Printf("systray error: unsupported key %q for menu shortcut\n", item.shortcutKey)
69 return ""
70 }
71 return strings.ToLower(item.shortcutKey)
72 }
73
74 // SetTemplateIcon sets the systray icon as a template icon (on Mac), falling back
75 // to a regular icon on other platforms.
76 // templateIconBytes and regularIconBytes should be the content of .ico for windows and
77 // .ico/.jpg/.png for other platforms.
78 func SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) {
79 cstr := (*C.char)(unsafe.Pointer(&templateIconBytes[0]))
80 C.setIcon(cstr, (C.int)(len(templateIconBytes)), true)
81 }
82
83 // SetIcon sets the icon of a menu item. Only works on macOS and Windows.
84 // iconBytes should be the content of .ico/.jpg/.png
85 func (item *MenuItem) SetIcon(iconBytes []byte) {
86 cstr := (*C.char)(unsafe.Pointer(&iconBytes[0]))
87 C.setMenuItemIcon(cstr, (C.int)(len(iconBytes)), C.int(item.id), false)
88 }
89
90 // SetIconFromFilePath sets the icon of a menu item from a file path.
91 // iconFilePath should be the path to a .ico for windows and .ico/.jpg/.png for other platforms.
92 func (item *MenuItem) SetIconFromFilePath(iconFilePath string) error {
93 iconBytes, err := os.ReadFile(iconFilePath)
94 if err != nil {
95 return fmt.Errorf("failed to read icon file: %v", err)
96 }
97 item.SetIcon(iconBytes)
98 return nil
99 }
100
101 // SetTemplateIcon sets the icon of a menu item as a template icon (on macOS). On Windows, it
102 // falls back to the regular icon bytes and on Linux it does nothing.
103 // templateIconBytes and regularIconBytes should be the content of .ico for windows and
104 // .ico/.jpg/.png for other platforms.
105 func (item *MenuItem) SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) {
106 cstr := (*C.char)(unsafe.Pointer(&templateIconBytes[0]))
107 C.setMenuItemIcon(cstr, (C.int)(len(templateIconBytes)), C.int(item.id), true)
108 }
109
110 // SetRemovalAllowed sets whether a user can remove the systray icon or not.
111 // This is only supported on macOS.
112 func SetRemovalAllowed(allowed bool) {
113 C.setRemovalAllowed((C.bool)(allowed))
114 }
115
116 func registerSystray() {
117 C.registerSystray()
118 }
119
120 func nativeLoop() {
121 C.nativeLoop()
122 }
123
124 func nativeEnd() {
125 C.nativeEnd()
126 }
127
128 func nativeStart() {
129 C.nativeStart()
130 }
131
132 func quit() {
133 C.quit()
134 }
135
136 func setInternalLoop(internal bool) {
137 C.setInternalLoop(C.bool(internal))
138 }
139
140 // SetIcon sets the systray icon.
141 // iconBytes should be the content of .ico for windows and .ico/.jpg/.png
142 // for other platforms.
143 func SetIcon(iconBytes []byte) {
144 cstr := (*C.char)(unsafe.Pointer(&iconBytes[0]))
145 C.setIcon(cstr, (C.int)(len(iconBytes)), false)
146 }
147
148 // SetIconFromFilePath sets the systray icon from a file path.
149 // iconFilePath should be the path to a .ico for windows and .ico/.jpg/.png for other platforms.
150 func SetIconFromFilePath(iconFilePath string) error {
151 bytes, err := os.ReadFile(iconFilePath)
152 if err != nil {
153 return fmt.Errorf("failed to read icon file: %v", err)
154 }
155 SetIcon(bytes)
156 return nil
157 }
158
159 // SetTitle sets the systray title, only available on Mac and Linux.
160 func SetTitle(title string) {
161 C.setTitle(C.CString(title))
162 }
163
164 // SetTooltip sets the systray tooltip to display on mouse hover of the tray icon,
165 // only available on Mac and Windows.
166 func SetTooltip(tooltip string) {
167 C.setTooltip(C.CString(tooltip))
168 }
169
170 func addOrUpdateMenuItem(item *MenuItem) {
171 var disabled C.short
172 if item.disabled {
173 disabled = 1
174 }
175 var checked C.short
176 if item.checked {
177 checked = 1
178 }
179 var isCheckable C.short
180 if item.isCheckable {
181 isCheckable = 1
182 }
183 var parentID uint32 = 0
184 if item.parent != nil {
185 parentID = item.parent.id
186 }
187 C.add_or_update_menu_item(
188 C.int(item.id),
189 C.int(parentID),
190 C.CString(item.title),
191 C.CString(item.tooltip),
192 C.CString(item.keyEquivalent()),
193 C.uint(item.shortcutMods),
194 disabled,
195 checked,
196 isCheckable,
197 )
198 }
199
200 func addSeparator(id uint32, parent uint32) {
201 C.add_separator(C.int(id), C.int(parent))
202 }
203
204 func hideMenuItem(item *MenuItem) {
205 C.hide_menu_item(
206 C.int(item.id),
207 )
208 }
209
210 func showMenuItem(item *MenuItem) {
211 C.show_menu_item(
212 C.int(item.id),
213 )
214 }
215
216 func removeMenuItem(item *MenuItem) {
217 C.remove_menu_item(
218 C.int(item.id),
219 )
220 }
221
222 func resetMenu() {
223 C.reset_menu()
224 }
225
226 //export systray_left_click
227 func systray_left_click() {
228 if fn := tappedLeft; fn != nil {
229 fn()
230 return
231 }
232
233 C.show_menu()
234 }
235
236 //export systray_right_click
237 func systray_right_click() {
238 if fn := tappedRight; fn != nil {
239 fn()
240 return
241 }
242
243 C.show_menu()
244 }
245
246 //export systray_ready
247 func systray_ready() {
248 systrayReady()
249 }
250
251 //export systray_on_exit
252 func systray_on_exit() {
253 runSystrayExit()
254 }
255
256 //export systray_menu_item_selected
257 func systray_menu_item_selected(cID C.int) {
258 systrayMenuItemSelected(uint32(cID))
259 }
260
261 //export systray_menu_will_open
262 func systray_menu_will_open() {
263 select {
264 case TrayOpenedCh <- struct{}{}:
265 default:
266 }
267 }
268
268 lines GO