Code optimization.
This commit is contained in:
1 parent
12c514aab0
commit
a121c26d63
11 files changed
+211
-37
No files matched your search
@@ -176,14 +176,19 @@ func testLinkedHeadingSoundGeometry(t *testing.T, ctx context.Context, adapter *
|
||||
}
|
||||
defer worker.Stop(context.Background())
|
||||
for _, step := range []struct {
|
||||
target review.DocumentTarget
|
||||
level int
|
||||
backwards bool
|
||||
name string
|
||||
}{
|
||||
{false, "Offscreen first topic"},
|
||||
{false, "Offscreen second topic"},
|
||||
{true, "Offscreen first topic"},
|
||||
{review.DocumentHeading, 6, false, "Offscreen first topic"},
|
||||
{review.DocumentHeading, 6, false, "Offscreen second topic"},
|
||||
{review.DocumentHeading, 6, true, "Offscreen first topic"},
|
||||
{review.DocumentLink, 0, false, "Offscreen first topic"},
|
||||
{review.DocumentLink, 0, false, "Offscreen second topic"},
|
||||
{review.DocumentLink, 0, true, "Offscreen first topic"},
|
||||
} {
|
||||
if err := worker.MoveDocument(review.DocumentHeading, 6, step.backwards); err != nil {
|
||||
if err := worker.MoveDocument(step.target, step.level, step.backwards); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
select {
|
||||
@@ -193,6 +198,9 @@ func testLinkedHeadingSoundGeometry(t *testing.T, ctx context.Context, adapter *
|
||||
t.Fatalf("scrolled heading result = %+v", result)
|
||||
}
|
||||
link := result.DocumentPosition.LinkedHeading
|
||||
if step.target == review.DocumentLink {
|
||||
link = &result.DocumentPosition.Snapshot
|
||||
}
|
||||
if link == nil || !link.SoundGeometry.ObjectKnown || !link.SoundGeometry.ReferenceKnown {
|
||||
t.Fatalf("scrolled link lacks fresh geometry: %+v", link)
|
||||
}
|
||||
|
||||
@@ -93,8 +93,10 @@ func (adapter *Adapter) CaptureDocumentSoundGeometry(ctx context.Context, object
|
||||
if !adapter.captureSound.Load() {
|
||||
return [2]SoundGeometry{}, nil
|
||||
}
|
||||
result, err := adapter.documentOperation(ctx, treeCaptureRequest{soundObjects: [2]ObjectReference{object, link}})
|
||||
return result.soundGeometry, err
|
||||
return retryDocumentSoundGeometry(ctx, link.Valid(), func(ctx context.Context) ([2]SoundGeometry, error) {
|
||||
result, err := adapter.documentOperation(ctx, treeCaptureRequest{soundObjects: [2]ObjectReference{object, link}})
|
||||
return result.soundGeometry, err
|
||||
})
|
||||
}
|
||||
|
||||
func (adapter *Adapter) captureNativeDocumentSoundGeometry(ctx context.Context, objects [2]ObjectReference) ([2]SoundGeometry, error) {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2026 Storm Dragon
|
||||
// Authors: Storm Dragon, Angus Kola
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package atspi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A successful ScrollTo can precede the browser's visibility/geometry update.
|
||||
// Retry missing geometry on the calling worker, leaving the native event owner
|
||||
// free to process those updates. Unsupported geometry gets at most 50ms of
|
||||
// extra waiting; native calls remain bounded by the caller's context.
|
||||
func retryDocumentSoundGeometry(ctx context.Context, linked bool, capture func(context.Context) ([2]SoundGeometry, error)) ([2]SoundGeometry, error) {
|
||||
for attempt := 0; ; attempt++ {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return [2]SoundGeometry{}, err
|
||||
}
|
||||
geometry, err := capture(ctx)
|
||||
if err != nil {
|
||||
return [2]SoundGeometry{}, err
|
||||
}
|
||||
ready := geometry[0].ObjectKnown && geometry[0].ReferenceKnown
|
||||
if linked {
|
||||
ready = ready && geometry[1].ObjectKnown && geometry[1].ReferenceKnown
|
||||
}
|
||||
if ready || attempt == 5 {
|
||||
return geometry, nil
|
||||
}
|
||||
timer := time.NewTimer(10 * time.Millisecond)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
timer.Stop()
|
||||
return [2]SoundGeometry{}, ctx.Err()
|
||||
case <-timer.C:
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2026 Storm Dragon
|
||||
// Authors: Storm Dragon, Angus Kola
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package atspi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDocumentSoundGeometryWaitsForBrowserScroll(t *testing.T) {
|
||||
ready := SoundGeometry{ObjectKnown: true, ReferenceKnown: true,
|
||||
Object: Rectangle{X: 18, Y: 147, Width: 102, Height: 13},
|
||||
Reference: Rectangle{Width: 800, Height: 600}}
|
||||
for _, linked := range []bool{false, true} {
|
||||
calls := 0
|
||||
geometry, err := retryDocumentSoundGeometry(context.Background(), linked, func(context.Context) ([2]SoundGeometry, error) {
|
||||
calls++
|
||||
// Scroll completion can reach the heading and link at different times.
|
||||
if calls == 1 {
|
||||
return [2]SoundGeometry{}, nil
|
||||
}
|
||||
if calls == 2 {
|
||||
return [2]SoundGeometry{ready, {}}, nil
|
||||
}
|
||||
return [2]SoundGeometry{ready, ready}, nil
|
||||
})
|
||||
wantCalls := 2
|
||||
if linked {
|
||||
wantCalls = 3
|
||||
}
|
||||
if err != nil || calls != wantCalls || geometry[0] != ready || linked && geometry[1] != ready {
|
||||
t.Fatalf("linked=%t: calls=%d geometry=%+v err=%v", linked, calls, geometry, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocumentSoundGeometryRetryBoundsAndCancellation(t *testing.T) {
|
||||
for _, scenario := range []string{"ready", "unavailable", "stale", "cancelled", "already cancelled"} {
|
||||
t.Run(scenario, func(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
if scenario == "already cancelled" {
|
||||
cancel()
|
||||
}
|
||||
calls := 0
|
||||
_, err := retryDocumentSoundGeometry(ctx, false, func(context.Context) ([2]SoundGeometry, error) {
|
||||
calls++
|
||||
switch scenario {
|
||||
case "ready":
|
||||
return [2]SoundGeometry{{ObjectKnown: true, ReferenceKnown: true}, {}}, nil
|
||||
case "stale":
|
||||
return [2]SoundGeometry{}, ErrStaleObject
|
||||
case "cancelled":
|
||||
cancel()
|
||||
}
|
||||
return [2]SoundGeometry{}, nil
|
||||
})
|
||||
wantCalls, wantErr := 1, error(nil)
|
||||
switch scenario {
|
||||
case "unavailable":
|
||||
wantCalls = 6
|
||||
case "stale":
|
||||
wantErr = ErrStaleObject
|
||||
case "cancelled":
|
||||
wantErr = context.Canceled
|
||||
case "already cancelled":
|
||||
wantCalls, wantErr = 0, context.Canceled
|
||||
}
|
||||
if calls != wantCalls || !errors.Is(err, wantErr) {
|
||||
t.Fatalf("calls=%d err=%v, want calls=%d err=%v", calls, err, wantCalls, wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -171,7 +171,7 @@ Foreground read_foreground(Display *display) {
|
||||
pthread_mutex_unlock(&error_mutex);
|
||||
return result;
|
||||
}
|
||||
static int cthulhu_display_active_window_xterm_match_unchecked(Display *display) {
|
||||
int cthulhu_focus_metadata_unchecked(Display *display, SteamFocusSample *steam) {
|
||||
Window root = DefaultRootWindow(display);
|
||||
Atom activeAtom = XInternAtom(display, "_NET_ACTIVE_WINDOW", False);
|
||||
Atom type = None;
|
||||
@@ -184,6 +184,10 @@ static int cthulhu_display_active_window_xterm_match_unchecked(Display *display)
|
||||
}
|
||||
Window window = ((Window *)data)[0];
|
||||
XFree(data);
|
||||
if (steam != NULL) {
|
||||
steam->known = 1;
|
||||
steam->window = window;
|
||||
}
|
||||
if (window == None || window == root) {
|
||||
return 0;
|
||||
}
|
||||
@@ -197,6 +201,10 @@ static int cthulhu_display_active_window_xterm_match_unchecked(Display *display)
|
||||
char *identifier = (char *)data;
|
||||
if (cthulhu_identifier_is_xterm(identifier)) matched = 1;
|
||||
char *classIdentifier = identifier + strlen(identifier) + 1;
|
||||
if (steam != NULL && (unsigned char *)classIdentifier < data + items) {
|
||||
steam->steam = strcmp(identifier, "steamwebhelper") == 0 &&
|
||||
strcmp(classIdentifier, "steam") == 0;
|
||||
}
|
||||
if (!matched && (unsigned char *)classIdentifier < data + items) {
|
||||
matched = cthulhu_identifier_is_xterm(classIdentifier);
|
||||
}
|
||||
@@ -236,7 +244,7 @@ int cthulhu_display_active_window_xterm_match(Display *display) {
|
||||
XSync(display,False);
|
||||
error_display=display; grab_error=0;
|
||||
previous_error_handler=XSetErrorHandler(command_error);
|
||||
int match=cthulhu_display_active_window_xterm_match_unchecked(display);
|
||||
int match=cthulhu_focus_metadata_unchecked(display, NULL);
|
||||
XSync(display,False);
|
||||
if (grab_error) match=0;
|
||||
XSetErrorHandler(previous_error_handler); error_display=NULL;
|
||||
|
||||
@@ -31,12 +31,16 @@ typedef struct {
|
||||
} Foreground;
|
||||
typedef struct { int known, pid, x, y; unsigned long window; } PointerPosition;
|
||||
typedef struct { int known, pid, steam, innerFocus; Window window, focus; } SteamFocusSample;
|
||||
typedef struct { int xterm; SteamFocusSample steam; } CommandFocusSample;
|
||||
|
||||
int cthulhu_active_window_is_xterm(void);
|
||||
int cthulhu_display_active_window_xterm_match(Display *display);
|
||||
Foreground read_foreground(Display *display);
|
||||
PointerPosition cthulhu_read_pointer(Display *display);
|
||||
SteamFocusSample cthulhu_read_steam_focus(Display *display);
|
||||
// Caller holds the X11 read guard; optional Steam metadata shares the same
|
||||
// active-window and WM_CLASS reads as the XTerm match.
|
||||
int cthulhu_focus_metadata_unchecked(Display *display, SteamFocusSample *steam);
|
||||
CommandFocusSample cthulhu_read_command_focus(Display *display, int steam_enabled);
|
||||
int cthulhu_restore_steam_focus(Display *display, Window wrapper, int pid, Window target);
|
||||
void cthulhu_x11_begin_read(Display *display);
|
||||
int cthulhu_x11_end_read(Display *display);
|
||||
|
||||
@@ -72,8 +72,7 @@ func (owner *commandOwner) run() {
|
||||
return
|
||||
}
|
||||
if time.Since(owner.lastFocusCheck) >= xtermFocusCheckInterval {
|
||||
owner.publishFocus()
|
||||
owner.check_steam_focus()
|
||||
owner.check_steam_focus(owner.publishFocus())
|
||||
}
|
||||
owner.resetForSecureInput()
|
||||
if !owner.syncGrabs() {
|
||||
@@ -104,16 +103,17 @@ func (owner *commandOwner) run() {
|
||||
}
|
||||
}
|
||||
|
||||
func (owner *commandOwner) publishFocus() {
|
||||
match := C.cthulhu_display_active_window_xterm_match(owner.device.display)
|
||||
func (owner *commandOwner) publishFocus() steamFocusSample {
|
||||
match, steam := read_command_focus(owner.device.display, !owner.listener.secure.Load() && owner.workerContext.Err() == nil)
|
||||
owner.lastFocusCheck = time.Now()
|
||||
if match == 0 || match == owner.lastFocusMatch {
|
||||
return
|
||||
if match == 0 || C.int(match) == owner.lastFocusMatch {
|
||||
return steam
|
||||
}
|
||||
owner.lastFocusMatch = match
|
||||
owner.lastFocusMatch = C.int(match)
|
||||
state := FocusState{Known: true, XTerm: match == 1}
|
||||
owner.listener.SetCommandGrabs(!state.XTerm)
|
||||
owner.listener.publishFocusState(state)
|
||||
return steam
|
||||
}
|
||||
|
||||
// Native xterm focus vetoes delayed accessibility requests to restore grabs.
|
||||
|
||||
@@ -61,19 +61,20 @@ static int steam_browser_child(Display *display, Window wrapper, int pid, Window
|
||||
return 0;
|
||||
}
|
||||
|
||||
SteamFocusSample cthulhu_read_steam_focus(Display *display) {
|
||||
SteamFocusSample sample = {0};
|
||||
CommandFocusSample cthulhu_read_command_focus(Display *display, int steam_enabled) {
|
||||
CommandFocusSample result = {0};
|
||||
SteamFocusSample *sample = &result.steam;
|
||||
cthulhu_x11_begin_read(display);
|
||||
sample.known = steam_active_window(display, &sample.window);
|
||||
if (sample.known && sample.window != None && steam_wrapper(display, sample.window)) {
|
||||
sample.pid = steam_window_pid(display, sample.window);
|
||||
sample.steam = sample.pid > 0;
|
||||
result.xterm = cthulhu_focus_metadata_unchecked(display, steam_enabled ? sample : NULL);
|
||||
if (sample->steam) {
|
||||
sample->pid = steam_window_pid(display, sample->window);
|
||||
sample->steam = sample->pid > 0;
|
||||
int revert;
|
||||
XGetInputFocus(display, &sample.focus, &revert);
|
||||
if (sample.steam) sample.innerFocus = steam_browser_child(display, sample.window, sample.pid, sample.focus);
|
||||
XGetInputFocus(display, &sample->focus, &revert);
|
||||
if (sample->steam) sample->innerFocus = steam_browser_child(display, sample->window, sample->pid, sample->focus);
|
||||
}
|
||||
if (cthulhu_x11_end_read(display)) sample = (SteamFocusSample){0};
|
||||
return sample;
|
||||
if (cthulhu_x11_end_read(display)) result = (CommandFocusSample){0};
|
||||
return result;
|
||||
}
|
||||
|
||||
int cthulhu_restore_steam_focus(Display *display, Window wrapper, int pid, Window target) {
|
||||
|
||||
@@ -56,9 +56,14 @@ func (repair *steamFocusRepair) observe(sample steamFocusSample) uint64 {
|
||||
return repair.target
|
||||
}
|
||||
|
||||
func read_steam_focus(display *C.Display) steamFocusSample {
|
||||
value := C.cthulhu_read_steam_focus(display)
|
||||
return steamFocusSample{window: uint64(value.window), focus: uint64(value.focus), pid: int(value.pid),
|
||||
func read_command_focus(display *C.Display, steamEnabled bool) (int, steamFocusSample) {
|
||||
var enabled C.int
|
||||
if steamEnabled {
|
||||
enabled = 1
|
||||
}
|
||||
result := C.cthulhu_read_command_focus(display, enabled)
|
||||
value := result.steam
|
||||
return int(result.xterm), steamFocusSample{window: uint64(value.window), focus: uint64(value.focus), pid: int(value.pid),
|
||||
known: value.known != 0, steam: value.steam != 0, innerFocus: value.innerFocus != 0}
|
||||
}
|
||||
|
||||
@@ -66,12 +71,11 @@ func restore_steam_focus(display *C.Display, window uint64, pid int, target uint
|
||||
return C.cthulhu_restore_steam_focus(display, C.Window(window), C.int(pid), C.Window(target)) != 0
|
||||
}
|
||||
|
||||
func (owner *commandOwner) check_steam_focus() {
|
||||
func (owner *commandOwner) check_steam_focus(sample steamFocusSample) {
|
||||
if owner.listener.secure.Load() || owner.workerContext.Err() != nil {
|
||||
owner.steamFocus.checksLeft = 0
|
||||
return
|
||||
}
|
||||
sample := read_steam_focus(owner.device.display)
|
||||
target := owner.steamFocus.observe(sample)
|
||||
if target == 0 || !owner.commandGrabsAllowed() || len(owner.held) != 0 || owner.device.active != 0 ||
|
||||
owner.learnMode || owner.uiSession != 0 || owner.ocrReview || owner.ocrPending {
|
||||
|
||||
@@ -66,7 +66,19 @@ func TestNativeSteamFocusRepair(t *testing.T) {
|
||||
t.Fatal("cannot open private X11 display")
|
||||
}
|
||||
defer reader.Close()
|
||||
sample := read_steam_focus(reader.display)
|
||||
readSteam := func() steamFocusSample {
|
||||
t.Helper()
|
||||
match, sample := read_command_focus(reader.display, true)
|
||||
if match != 2 {
|
||||
t.Fatalf("Steam/ordinary window XTerm match = %d", match)
|
||||
}
|
||||
return sample
|
||||
}
|
||||
match, disabled := read_command_focus(reader.display, false)
|
||||
if match != 2 || disabled != (steamFocusSample{}) {
|
||||
t.Fatalf("disabled Steam sampling = %d, %+v", match, disabled)
|
||||
}
|
||||
sample := readSteam()
|
||||
if !sample.steam || !sample.innerFocus || sample.focus != windows["browser"] {
|
||||
t.Fatalf("initial browser focus not recognized: %+v", sample)
|
||||
}
|
||||
@@ -75,12 +87,16 @@ func TestNativeSteamFocusRepair(t *testing.T) {
|
||||
roundTrip := func() uint64 {
|
||||
t.Helper()
|
||||
command("activate", "other")
|
||||
repair.observe(read_steam_focus(reader.display))
|
||||
other := readSteam()
|
||||
if !other.known || other.window != windows["other"] || other.steam || other.focus != 0 || other.pid != 0 {
|
||||
t.Fatalf("ordinary window queried Steam details: %+v", other)
|
||||
}
|
||||
repair.observe(other)
|
||||
command("activate", "steam")
|
||||
if target := repair.observe(read_steam_focus(reader.display)); target != 0 {
|
||||
if target := repair.observe(readSteam()); target != 0 {
|
||||
t.Fatal("Steam restoration grace sample was skipped")
|
||||
}
|
||||
return repair.observe(read_steam_focus(reader.display))
|
||||
return repair.observe(readSteam())
|
||||
}
|
||||
target := roundTrip()
|
||||
if target != windows["browser"] || !restore_steam_focus(reader.display, sample.window, sample.pid, target) {
|
||||
@@ -107,7 +123,7 @@ func TestNativeSteamFocusRepair(t *testing.T) {
|
||||
t.Fatalf("Escape was not delivered to restored browser: %+v", events)
|
||||
}
|
||||
command("focus", "popup")
|
||||
if target := repair.observe(read_steam_focus(reader.display)); target != 0 {
|
||||
if target := repair.observe(readSteam()); target != 0 {
|
||||
t.Fatal("valid popup focus was overridden")
|
||||
}
|
||||
target = roundTrip()
|
||||
@@ -147,7 +163,17 @@ func TestNativeSteamFocusRepair(t *testing.T) {
|
||||
t.Fatal("repair focused a destroyed popup")
|
||||
}
|
||||
command("class", "steam")
|
||||
if read_steam_focus(reader.display).steam || restore_steam_focus(reader.display, sample.window, sample.pid, windows["browser"]) {
|
||||
if readSteam().steam || restore_steam_focus(reader.display, sample.window, sample.pid, windows["browser"]) {
|
||||
t.Fatal("repair changed focus in an ordinary application")
|
||||
}
|
||||
command("xterm", "steam")
|
||||
match, terminal := read_command_focus(reader.display, true)
|
||||
if match != 1 || !terminal.known || terminal.steam || terminal.focus != 0 {
|
||||
t.Fatalf("XTerm class change = %d, %+v", match, terminal)
|
||||
}
|
||||
command("unknown", "steam")
|
||||
match, unknown := read_command_focus(reader.display, true)
|
||||
if match != 0 || unknown != (steamFocusSample{}) {
|
||||
t.Fatalf("unknown foreground = %d, %+v", match, unknown)
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,10 @@ def main() -> None:
|
||||
window.destroy()
|
||||
elif action == "class":
|
||||
window.set_wm_class("example", "Example")
|
||||
elif action == "xterm":
|
||||
window.set_wm_class("xterm", "XTerm")
|
||||
elif action == "unknown":
|
||||
root.delete_property(activeAtom)
|
||||
elif action == "pid":
|
||||
window.change_property(pidAtom, Xatom.CARDINAL, 32, [os.getpid() + 1])
|
||||
elif action == "events":
|
||||
|
||||
Reference in new issue
Block a user