detached 18292b3

improve(webkit): debounce keyboard events and add rendering backend detection

commit
18292b32e3a239d70071d8d638faa575444d41ec
author
bamen · · 2025-09-16 19:00 UTC
committer
bamen · · 2025-09-16 21:07 UTC
parent
eac22c5
signature
unsigned
1 file · +67 -2
M pkg/webkit/keyboard_cgo.go +67 -2
@@ -78,8 +78,10 @@ static void attach_mouse_legacy(GtkWidget* widget, unsigned long id) {
7878 import "C"
7979
8080 import (
81+ "encoding/json"
8182 "fmt"
8283 "log"
84+ "strings"
8385 "sync"
8486 "sync/atomic"
8587 "time"
@@ -149,10 +151,11 @@ func dispatchAccelerator(uid uintptr, keyval uint, state uint) {
149151 shift := (state & uint(C.GDK_SHIFT_MASK)) != 0
150152 // Map keyval to string names
151153 var keyName string
154+ logMinusEvent := false
152155 switch keyval {
153156 case uint(C.GDK_KEY_minus), uint(C.GDK_KEY_KP_Subtract), uint(C.GDK_KEY_underscore):
154157 // Normalize minus; log raw detection for diagnostics
155- log.Printf("[accelerator-raw] minus key detected ctrl=%v alt=%v keyval=0x%x", ctrl, alt, keyval)
158+ logMinusEvent = true
156159 keyName = "-"
157160 case uint(C.GDK_KEY_equal), uint(C.GDK_KEY_KP_Add):
158161 keyName = "="
@@ -182,6 +185,16 @@ func dispatchAccelerator(uid uintptr, keyval uint, state uint) {
182185 return
183186 }
184187
188+ now := time.Now()
189+ rawKey := fmt.Sprintf("raw:%v:%v:%v:%s", ctrl, alt, shift, keyName)
190+ if shouldDebounceShortcut(uid, rawKey, now) {
191+ return
192+ }
193+
194+ if logMinusEvent {
195+ log.Printf("[accelerator-raw] minus key detected ctrl=%v alt=%v keyval=0x%x", ctrl, alt, keyval)
196+ }
197+
185198 // Candidate accelerator strings in order
186199 candidates := []string{keyName}
187200 if ctrl && shift {
@@ -211,7 +224,6 @@ func dispatchAccelerator(uid uintptr, keyval uint, state uint) {
211224 normalized = ""
212225 }
213226
214- now := time.Now()
215227 // Forward normalized shortcut to GUI KeyboardService via DOM event if present
216228 if normalized != "" && !shouldDebounceShortcut(uid, "dom:"+normalized, now) {
217229 regMu.RLock()
@@ -369,6 +381,12 @@ func goOnUcmMessage(id C.ulong, json *C.char) {
369381 return
370382 }
371383 goPayload := C.GoString(json)
384+
385+ // Handle rendering backend detection messages specially
386+ if handleRenderingBackendMessage(goPayload) {
387+ return
388+ }
389+
372390 vw.dispatchScriptMessage(goPayload)
373391 }
374392
@@ -422,3 +440,50 @@ func goQuitMainLoop() {
422440 log.Printf("[webkit] Window close requested - quitting main loop")
423441 QuitMainLoop()
424442 }
443+
444+// handleRenderingBackendMessage processes rendering backend detection messages
445+func handleRenderingBackendMessage(payload string) bool {
446+ // Parse JSON to check if this is a rendering backend detection message
447+ if !strings.Contains(payload, "rendering_backend_detection") {
448+ return false
449+ }
450+
451+ // Simple JSON parsing for the specific structure we expect
452+ // {"type":"rendering_backend_detection","data":{...}}
453+ var msg struct {
454+ Type string `json:"type"`
455+ Data struct {
456+ WebGLAvailable bool `json:"webgl_available"`
457+ Renderer string `json:"renderer"`
458+ Vendor string `json:"vendor"`
459+ Version string `json:"version"`
460+ ShadingLanguageVersion string `json:"shading_language_version"`
461+ MaxTextureSize int `json:"max_texture_size"`
462+ HardwareAccelerated bool `json:"hardware_accelerated"`
463+ } `json:"data"`
464+ }
465+
466+ if err := json.Unmarshal([]byte(payload), &msg); err != nil {
467+ log.Printf("[webkit] Failed to parse rendering backend message: %v", err)
468+ return false
469+ }
470+
471+ if msg.Type != "rendering_backend_detection" {
472+ return false
473+ }
474+
475+ // Log the detected rendering backend information
476+ backendType := "SOFTWARE"
477+ if msg.Data.HardwareAccelerated {
478+ backendType = "HARDWARE"
479+ }
480+
481+ log.Printf("[webkit] Detected rendering backend: %s", backendType)
482+ log.Printf("[webkit] WebGL available: %v", msg.Data.WebGLAvailable)
483+ log.Printf("[webkit] Renderer: %s", msg.Data.Renderer)
484+ log.Printf("[webkit] Vendor: %s", msg.Data.Vendor)
485+ log.Printf("[webkit] Version: %s", msg.Data.Version)
486+ log.Printf("[webkit] Max texture size: %d", msg.Data.MaxTextureSize)
487+
488+ return true
489+}
@@ -78,8 +78,10 @@ static void attach_mouse_legacy(GtkWidget* widget, unsigned long id) {
78 import "C"78 import "C"
79 79
80 import (80 import (
81+ "encoding/json"
81 "fmt"82 "fmt"
82 "log"83 "log"
84+ "strings"
83 "sync"85 "sync"
84 "sync/atomic"86 "sync/atomic"
85 "time"87 "time"
@@ -149,10 +151,11 @@ func dispatchAccelerator(uid uintptr, keyval uint, state uint) {
149 shift := (state & uint(C.GDK_SHIFT_MASK)) != 0151 shift := (state & uint(C.GDK_SHIFT_MASK)) != 0
150 // Map keyval to string names152 // Map keyval to string names
151 var keyName string153 var keyName string
154+ logMinusEvent := false
152 switch keyval {155 switch keyval {
153 case uint(C.GDK_KEY_minus), uint(C.GDK_KEY_KP_Subtract), uint(C.GDK_KEY_underscore):156 case uint(C.GDK_KEY_minus), uint(C.GDK_KEY_KP_Subtract), uint(C.GDK_KEY_underscore):
154 // Normalize minus; log raw detection for diagnostics157 // Normalize minus; log raw detection for diagnostics
155- log.Printf("[accelerator-raw] minus key detected ctrl=%v alt=%v keyval=0x%x", ctrl, alt, keyval)158+ logMinusEvent = true
156 keyName = "-"159 keyName = "-"
157 case uint(C.GDK_KEY_equal), uint(C.GDK_KEY_KP_Add):160 case uint(C.GDK_KEY_equal), uint(C.GDK_KEY_KP_Add):
158 keyName = "="161 keyName = "="
@@ -182,6 +185,16 @@ func dispatchAccelerator(uid uintptr, keyval uint, state uint) {
182 return185 return
183 }186 }
184 187
188+ now := time.Now()
189+ rawKey := fmt.Sprintf("raw:%v:%v:%v:%s", ctrl, alt, shift, keyName)
190+ if shouldDebounceShortcut(uid, rawKey, now) {
191+ return
192+ }
193+
194+ if logMinusEvent {
195+ log.Printf("[accelerator-raw] minus key detected ctrl=%v alt=%v keyval=0x%x", ctrl, alt, keyval)
196+ }
197+
185 // Candidate accelerator strings in order198 // Candidate accelerator strings in order
186 candidates := []string{keyName}199 candidates := []string{keyName}
187 if ctrl && shift {200 if ctrl && shift {
@@ -211,7 +224,6 @@ func dispatchAccelerator(uid uintptr, keyval uint, state uint) {
211 normalized = ""224 normalized = ""
212 }225 }
213 226
214- now := time.Now()
215 // Forward normalized shortcut to GUI KeyboardService via DOM event if present227 // Forward normalized shortcut to GUI KeyboardService via DOM event if present
216 if normalized != "" && !shouldDebounceShortcut(uid, "dom:"+normalized, now) {228 if normalized != "" && !shouldDebounceShortcut(uid, "dom:"+normalized, now) {
217 regMu.RLock()229 regMu.RLock()
@@ -369,6 +381,12 @@ func goOnUcmMessage(id C.ulong, json *C.char) {
369 return381 return
370 }382 }
371 goPayload := C.GoString(json)383 goPayload := C.GoString(json)
384+
385+ // Handle rendering backend detection messages specially
386+ if handleRenderingBackendMessage(goPayload) {
387+ return
388+ }
389+
372 vw.dispatchScriptMessage(goPayload)390 vw.dispatchScriptMessage(goPayload)
373 }391 }
374 392
@@ -422,3 +440,50 @@ func goQuitMainLoop() {
422 log.Printf("[webkit] Window close requested - quitting main loop")440 log.Printf("[webkit] Window close requested - quitting main loop")
423 QuitMainLoop()441 QuitMainLoop()
424 }442 }
443+
444+// handleRenderingBackendMessage processes rendering backend detection messages
445+func handleRenderingBackendMessage(payload string) bool {
446+ // Parse JSON to check if this is a rendering backend detection message
447+ if !strings.Contains(payload, "rendering_backend_detection") {
448+ return false
449+ }
450+
451+ // Simple JSON parsing for the specific structure we expect
452+ // {"type":"rendering_backend_detection","data":{...}}
453+ var msg struct {
454+ Type string `json:"type"`
455+ Data struct {
456+ WebGLAvailable bool `json:"webgl_available"`
457+ Renderer string `json:"renderer"`
458+ Vendor string `json:"vendor"`
459+ Version string `json:"version"`
460+ ShadingLanguageVersion string `json:"shading_language_version"`
461+ MaxTextureSize int `json:"max_texture_size"`
462+ HardwareAccelerated bool `json:"hardware_accelerated"`
463+ } `json:"data"`
464+ }
465+
466+ if err := json.Unmarshal([]byte(payload), &msg); err != nil {
467+ log.Printf("[webkit] Failed to parse rendering backend message: %v", err)
468+ return false
469+ }
470+
471+ if msg.Type != "rendering_backend_detection" {
472+ return false
473+ }
474+
475+ // Log the detected rendering backend information
476+ backendType := "SOFTWARE"
477+ if msg.Data.HardwareAccelerated {
478+ backendType = "HARDWARE"
479+ }
480+
481+ log.Printf("[webkit] Detected rendering backend: %s", backendType)
482+ log.Printf("[webkit] WebGL available: %v", msg.Data.WebGLAvailable)
483+ log.Printf("[webkit] Renderer: %s", msg.Data.Renderer)
484+ log.Printf("[webkit] Vendor: %s", msg.Data.Vendor)
485+ log.Printf("[webkit] Version: %s", msg.Data.Version)
486+ log.Printf("[webkit] Max texture size: %d", msg.Data.MaxTextureSize)
487+
488+ return true
489+}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close