shortcuts_cgo.go RawHistoryFolder
1//go:build webkit_cgo
2
3package webkit
4
5/*
6#cgo pkg-config: gtk4 webkitgtk-6.0
7#include <gtk/gtk.h>
8#include <stdint.h>
9#include <stdlib.h>
10
11extern void goHandleWindowShortcut(uintptr_t handle);
12
13static GtkShortcutController* create_global_shortcut_controller(GtkWindow* window) {
14 if (!window) {
15 return NULL;
16 }
17 GtkEventController* event_controller = gtk_shortcut_controller_new();
18 if (!event_controller) {
19 return NULL;
20 }
21 GtkShortcutController* controller = GTK_SHORTCUT_CONTROLLER(event_controller);
22 gtk_shortcut_controller_set_scope(controller, GTK_SHORTCUT_SCOPE_GLOBAL);
23 gtk_widget_add_controller(GTK_WIDGET(window), event_controller);
24 return controller;
25}
26
27static gboolean shortcut_callback_bridge(GtkWidget* widget, GVariant* args, gpointer user_data) {
28 (void)widget;
29 (void)args;
30 uintptr_t handle = (uintptr_t)user_data;
31 if (handle == 0) {
32 return FALSE;
33 }
34 goHandleWindowShortcut(handle);
35 return TRUE; // Indicate we handled the shortcut
36}
37
38static void add_shortcut_to_controller(GtkShortcutController* controller,
39 const char* trigger_str,
40 uintptr_t handle) {
41 if (!controller || !trigger_str || handle == 0) {
42 return;
43 }
44 GtkShortcutTrigger* trigger = gtk_shortcut_trigger_parse_string(trigger_str);
45 if (!trigger) {
46 return;
47 }
48 GtkShortcutAction* action = gtk_callback_action_new((GtkShortcutFunc)shortcut_callback_bridge,
49 (gpointer)handle,
50 NULL);
51 if (!action) {
52 g_object_unref(trigger);
53 return;
54 }
55 GtkShortcut* shortcut = gtk_shortcut_new(trigger, action);
56 gtk_shortcut_controller_add_shortcut(controller, shortcut);
57}
58*/
59import "C"
60
61import (
62 "log"
63 "sync"
64 "sync/atomic"
65 "unsafe"
66)
67
68var (
69 globalShortcutMu sync.Mutex
70 globalShortcutCounter uint64
71 globalShortcutCallbacks = make(map[uintptr]func())
72)
73
74func registerWindowShortcutCallback(cb func()) uintptr {
75 if cb == nil {
76 return 0
77 }
78 id := uintptr(atomic.AddUint64(&globalShortcutCounter, 1))
79 if id == 0 {
80 // Should never happen, but guard against wrapping to 0
81 id = uintptr(atomic.AddUint64(&globalShortcutCounter, 1))
82 }
83 globalShortcutMu.Lock()
84 globalShortcutCallbacks[id] = cb
85 globalShortcutMu.Unlock()
86 return id
87}
88
89func unregisterWindowShortcutCallback(id uintptr) {
90 if id == 0 {
91 return
92 }
93 globalShortcutMu.Lock()
94 delete(globalShortcutCallbacks, id)
95 globalShortcutMu.Unlock()
96}
97
98func invokeWindowShortcutCallback(id uintptr) {
99 if id == 0 {
100 return
101 }
102 globalShortcutMu.Lock()
103 cb := globalShortcutCallbacks[id]
104 globalShortcutMu.Unlock()
105 if cb == nil {
106 log.Printf("[shortcuts] no handler for shortcut id=%d", id)
107 return
108 }
109 cb()
110}
111
112//export goHandleWindowShortcut
113func goHandleWindowShortcut(handle C.uintptr_t) {
114 invokeWindowShortcutCallback(uintptr(handle))
115}
116
117// Go wrapper functions for CGO - these can be called from other Go files
118
119// CreateGlobalShortcutController creates a GTK4 global shortcut controller for a window
120func CreateGlobalShortcutController(window uintptr) uintptr {
121 if window == 0 {
122 return 0
123 }
124 controller := C.create_global_shortcut_controller((*C.GtkWindow)(unsafe.Pointer(window)))
125 return uintptr(unsafe.Pointer(controller))
126}
127
128// AddShortcutToController adds a shortcut to the controller with a callback handle
129func AddShortcutToController(controller uintptr, key string, handle uintptr) error {
130 if controller == 0 || key == "" || handle == 0 {
131 return ErrNotImplemented
132 }
133
134 keyStr := C.CString(key)
135 defer C.free(unsafe.Pointer(keyStr))
136
137 C.add_shortcut_to_controller(
138 (*C.GtkShortcutController)(unsafe.Pointer(controller)),
139 keyStr,
140 C.uintptr_t(handle),
141 )
142
143 return nil
144}