1//go:build webkit_cgo
2
3package webkit
4
5import (
6 "log"
7 "strings"
8 "unsafe"
9)
10
11// No C imports needed - using Go wrapper functions from shortcuts_cgo.go
12
13// WindowShortcuts manages global shortcuts at the window level
14type WindowShortcuts struct {
15 window *Window
16 controller uintptr // *C.GtkShortcutController
17 callbacks map[string]uintptr
18}
19
20// InitializeGlobalShortcuts sets up the GTK4 global shortcut controller
21func (w *Window) InitializeGlobalShortcuts() *WindowShortcuts {
22 if w.win == nil {
23 log.Printf("[window-shortcuts] Cannot initialize - invalid window")
24 return nil
25 }
26
27 controller := CreateGlobalShortcutController(uintptr(unsafe.Pointer(w.win)))
28 if controller == 0 {
29 log.Printf("[window-shortcuts] Failed to create shortcut controller")
30 return nil
31 }
32
33 ws := &WindowShortcuts{
34 window: w,
35 controller: controller,
36 callbacks: make(map[string]uintptr),
37 }
38
39 log.Printf("[window-shortcuts] Global shortcut controller initialized")
40 return ws
41}
42
43// RegisterGlobalShortcut registers a shortcut at the window level with global scope
44func (ws *WindowShortcuts) RegisterGlobalShortcut(key string, callback func()) error {
45 if ws == nil || ws.controller == 0 {
46 return ErrNotImplemented
47 }
48
49 // Convert key format (e.g., "ctrl+l" -> "<Control>l")
50 gtkKey := convertToGtkFormat(key)
51 if gtkKey == "" {
52 log.Printf("[window-shortcuts] Invalid key format: %s", key)
53 return ErrNotImplemented
54 }
55
56 // Register callback and get handle
57 handle := registerWindowShortcutCallback(callback)
58 if handle == 0 {
59 log.Printf("[window-shortcuts] Failed to register callback for %s", key)
60 return ErrNotImplemented
61 }
62
63 // Store handle for cleanup
64 ws.callbacks[key] = handle
65
66 // Register with GTK using Go wrapper function
67 if err := AddShortcutToController(ws.controller, gtkKey, handle); err != nil {
68 log.Printf("[window-shortcuts] Failed to add shortcut to controller: %v", err)
69 unregisterWindowShortcutCallback(handle)
70 delete(ws.callbacks, key)
71 return err
72 }
73
74 log.Printf("[window-shortcuts] Registered global shortcut: %s -> %s", key, gtkKey)
75 return nil
76}
77
78// Cleanup unregisters all shortcuts and cleans up resources
79func (ws *WindowShortcuts) Cleanup() {
80 if ws == nil {
81 return
82 }
83
84 // Unregister all callbacks
85 for key, handle := range ws.callbacks {
86 unregisterWindowShortcutCallback(handle)
87 log.Printf("[window-shortcuts] Unregistered shortcut: %s", key)
88 }
89
90 ws.callbacks = nil
91 ws.controller = 0
92}
93
94// convertToGtkFormat converts common key formats to GTK shortcut format
95func convertToGtkFormat(key string) string {
96 // Convert from electron/webkit format to GTK format
97 key = strings.ToLower(key)
98
99 // Handle cmdorctrl -> Control
100 key = strings.ReplaceAll(key, "cmdorctrl+", "ctrl+")
101 key = strings.ReplaceAll(key, "cmd+", "ctrl+")
102
103 // Build GTK format
104 var parts []string
105 components := strings.Split(key, "+")
106
107 for i, part := range components {
108 if i == len(components)-1 {
109 // Last part is the key
110 switch part {
111 case "f12":
112 parts = append(parts, "F12")
113 case "f5":
114 parts = append(parts, "F5")
115 case "l":
116 parts = append(parts, "l")
117 case "f":
118 parts = append(parts, "f")
119 case "r":
120 parts = append(parts, "r")
121 case "c":
122 parts = append(parts, "c")
123 case "left", "arrowleft":
124 parts = append(parts, "Left")
125 case "right", "arrowright":
126 parts = append(parts, "Right")
127 case "up", "arrowup":
128 parts = append(parts, "Up")
129 case "down", "arrowdown":
130 parts = append(parts, "Down")
131 default:
132 parts = append(parts, part)
133 }
134 } else {
135 // Modifier
136 switch part {
137 case "ctrl":
138 parts = append(parts, "<Control>")
139 case "shift":
140 parts = append(parts, "<Shift>")
141 case "alt":
142 parts = append(parts, "<Alt>")
143 case "super":
144 parts = append(parts, "<Super>")
145 }
146 }
147 }
148
149 return strings.Join(parts, "")
150}