Refine Wine dialog event presentation

This commit is contained in:
Storm Dragon committed 2026-07-17 19:51:33 -04:00
1 parent 1fd9acd94b
commit f6b7f2ad71
5 files changed
+351 -13

No files matched your search

+7
View File
@@ -304,6 +304,13 @@ class Backend:
def _migrateSettings(self, settingsDict):
"""Migrate old setting names to new ones."""
migrationKey = 'wineAccessibilityPluginMigrated'
activePlugins = settingsDict.get('activePlugins')
if isinstance(activePlugins, list) and migrationKey not in settingsDict:
if 'WineAccessibility' not in activePlugins:
activePlugins.append('WineAccessibility')
settingsDict[migrationKey] = True
# Migration: enableSpeechIndentation -> enableIndentation
if 'enableSpeechIndentation' in settingsDict and 'enableIndentation' not in settingsDict:
settingsDict['enableIndentation'] = settingsDict['enableSpeechIndentation']
+85 -5
View File
@@ -25,6 +25,7 @@ import contextlib
import enum
import inspect
import os
import time
import types
import typing
import xml.etree.ElementTree as ET
@@ -641,6 +642,8 @@ class _InterfaceBuilder:
class CthulhuDBusServiceInterface(Publishable):
"""Internal D-Bus service object that handles D-Bus specifics."""
_WINE_FOCUS_DEBOUNCE_MS = 50
def for_publication(self):
"""Returns the D-Bus interface XML for publication."""
@@ -744,15 +747,92 @@ class CthulhuDBusServiceInterface(Publishable):
) -> bool:
"""Presents a normalized accessibility event received from Wine."""
del sourceId, eventType, states
del states
eventType = eventType.casefold()
debug.print_message(
debug.LEVEL_INFO,
(
"WINE ACCESS EVENT: "
f"type={eventType} source={sourceId} name={name!r} "
f"role={role!r} value={value!r} window={windowTitle!r}"
),
True,
)
if eventType == "focus":
self._wineAccessibleFocusSource = sourceId
else:
if eventType not in {"value", "selection"}:
return False
focusedSource = getattr(self, "_wineAccessibleFocusSource", None)
focusedWindow = getattr(self, "_wineAccessibleWindowTitle", None)
pendingFocus = getattr(self, "_wineAccessiblePendingFocus", None)
if focusedWindow is None and pendingFocus is not None:
focusedWindow = pendingFocus[2]
if sourceId != focusedSource:
if eventType != "selection":
return False
if not windowTitle or windowTitle != focusedWindow:
return False
if role.casefold() == "client":
role = ""
parts = [part for part in (name, role, value, description) if part]
if position > 0 and count > 0:
parts.append(f"{position} of {count}")
if windowTitle and windowTitle not in parts:
parts.append(windowTitle)
if not parts:
if not parts and not (eventType == "focus" and windowTitle):
return False
return self.PresentMessage(", ".join(parts))
message = ", ".join(parts)
now = time.monotonic()
signature = (sourceId, message or windowTitle)
lastSignature = getattr(self, "_wineAccessibleLastSignature", None)
lastTime = getattr(self, "_wineAccessibleLastTime", 0.0)
if signature == lastSignature and now - lastTime < 0.1:
if eventType == "focus":
self._wineAccessiblePendingFocus = None
return False
if eventType == "focus":
self._wineAccessiblePendingFocus = (signature, parts, windowTitle)
if not getattr(self, "_wineAccessibleFocusTimeoutId", 0):
self._wineAccessibleFocusTimeoutId = GLib.timeout_add(
self._WINE_FOCUS_DEBOUNCE_MS,
self._present_pending_accessible_focus,
)
return True
pending = getattr(self, "_wineAccessiblePendingFocus", None)
if pending is not None and (
pending[0][0] == sourceId or (windowTitle and pending[2] == windowTitle)
):
self._wineAccessiblePendingFocus = (signature, parts, pending[2])
return True
self._wineAccessibleLastSignature = signature
self._wineAccessibleLastTime = now
return self.PresentMessage(message)
def _present_pending_accessible_focus(self) -> bool:
"""Presents the final focus event after Wine's transient focus burst."""
pending = getattr(self, "_wineAccessiblePendingFocus", None)
self._wineAccessiblePendingFocus = None
self._wineAccessibleFocusTimeoutId = 0
if pending is None:
return False
signature, parts, windowTitle = pending
self._wineAccessibleLastSignature = signature
self._wineAccessibleLastTime = time.monotonic()
parts = list(parts)
if windowTitle:
previousTitle = getattr(self, "_wineAccessibleWindowTitle", None)
self._wineAccessibleWindowTitle = windowTitle
if windowTitle != previousTitle and windowTitle not in parts:
parts.append(windowTitle)
if parts:
self.PresentMessage(", ".join(parts))
return False
def GetVersion(self) -> str: # pylint: disable=invalid-name
"""Returns Cthulhu's version and revision if available."""