Cthulhu is no longer a fork. Now it is a screen reader written in Golang.
This commit is contained in:
1 parent
a998fe67de
commit
069fc674dc
708 files changed
+112482
-532
No files matched your search
@@ -0,0 +1,125 @@
|
||||
"""Painted OCR text with accessible focus targets on a private Xorg display."""
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import cairo
|
||||
|
||||
import gi
|
||||
gi.require_version("Gtk", "3.0")
|
||||
gi.require_version("Gdk", "3.0")
|
||||
gi.require_version("GdkX11", "3.0")
|
||||
gi.require_version("GdkPixbuf", "2.0")
|
||||
from gi.repository import Gdk, GdkPixbuf, GdkX11, GLib, Gtk
|
||||
from Xlib import X, Xatom, display
|
||||
|
||||
|
||||
def main() -> None:
|
||||
window = Gtk.Window(title="OCR session fixture")
|
||||
window.set_decorated(False)
|
||||
window.move(20, 20)
|
||||
fixed = Gtk.Fixed()
|
||||
window.add(fixed)
|
||||
painted = Gtk.DrawingArea()
|
||||
painted.set_size_request(640, 220)
|
||||
painted.set_can_focus(True)
|
||||
painted.get_accessible().set_name("Painted OCR target")
|
||||
imagePath = Path(__file__).resolve().parents[1] / "internal/ocr/testdata/painted-text.png"
|
||||
pixels = GdkPixbuf.Pixbuf.new_from_file(str(imagePath))
|
||||
|
||||
def draw(_widget: Gtk.Widget, context: object) -> bool:
|
||||
Gdk.cairo_set_source_pixbuf(context, pixels, 0, 0)
|
||||
context.paint()
|
||||
return False
|
||||
|
||||
painted.connect("draw", draw)
|
||||
fixed.put(painted, 0, 0)
|
||||
password = Gtk.Entry()
|
||||
password.set_visibility(False)
|
||||
password.get_accessible().set_name("Protected OCR focus")
|
||||
fixed.put(password, 0, 230)
|
||||
other = Gtk.Button(label="Other OCR focus")
|
||||
fixed.put(other, 280, 230)
|
||||
window.show_all()
|
||||
painted.grab_focus()
|
||||
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
||||
clipboard.set_image(pixels)
|
||||
connection = display.Display()
|
||||
root = connection.screen().root
|
||||
clientId = GdkX11.X11Window.get_xid(window.get_window())
|
||||
client = connection.create_resource_object("window", clientId)
|
||||
root.change_property(connection.intern_atom("_NET_ACTIVE_WINDOW"), Xatom.WINDOW, 32, [clientId])
|
||||
client.change_property(connection.intern_atom("_NET_WM_PID"), Xatom.CARDINAL, 32, [os.getpid()])
|
||||
client.set_input_focus(X.RevertToParent, X.CurrentTime)
|
||||
connection.sync()
|
||||
activeClient = client
|
||||
rawClient = None
|
||||
|
||||
def show_inaccessible_window() -> None:
|
||||
nonlocal activeClient, rawClient
|
||||
# A raw X11 client deliberately has no AT-SPI object or focused widget.
|
||||
surface = cairo.ImageSurface.create_from_png(str(imagePath))
|
||||
width, height = surface.get_width(), surface.get_height()
|
||||
rawClient = root.create_window(20, 20, width, height, 0,
|
||||
connection.screen().root_depth,
|
||||
X.InputOutput, X.CopyFromParent,
|
||||
background_pixel=0xFFFFFF)
|
||||
rawClient.set_wm_name("Inaccessible OCR fixture")
|
||||
rawClient.set_wm_class("example", "example")
|
||||
rawClient.change_property(connection.intern_atom("_NET_WM_PID"),
|
||||
Xatom.CARDINAL, 32, [os.getpid()])
|
||||
rawClient.map()
|
||||
rawClient.configure(stack_mode=X.Above)
|
||||
graphics = rawClient.create_gc()
|
||||
data = bytes(surface.get_data())
|
||||
stride = surface.get_stride()
|
||||
for row in range(0, height, 16):
|
||||
rows = min(16, height - row)
|
||||
rawClient.put_image(graphics, 0, row, width, rows, X.ZPixmap,
|
||||
connection.screen().root_depth, 0,
|
||||
data[row * stride:(row + rows) * stride])
|
||||
graphics.free()
|
||||
root.change_property(connection.intern_atom("_NET_ACTIVE_WINDOW"),
|
||||
Xatom.WINDOW, 32, [rawClient.id])
|
||||
rawClient.set_input_focus(X.RevertToParent, X.CurrentTime)
|
||||
activeClient = rawClient
|
||||
|
||||
def state() -> bool:
|
||||
connection.sync()
|
||||
print(json.dumps({"window": activeClient.id, "focus": connection.get_input_focus().focus.id}), flush=True)
|
||||
return False
|
||||
|
||||
def command_ready(_source: object, condition: int) -> bool:
|
||||
if condition & GLib.IO_HUP:
|
||||
Gtk.main_quit()
|
||||
return False
|
||||
line = sys.stdin.readline()
|
||||
if not line:
|
||||
Gtk.main_quit()
|
||||
return False
|
||||
action = json.loads(line)["action"]
|
||||
if action == "painted":
|
||||
painted.grab_focus()
|
||||
elif action == "password":
|
||||
password.grab_focus()
|
||||
elif action == "other":
|
||||
other.grab_focus()
|
||||
elif action == "clipboard-text":
|
||||
clipboard.set_text("Generic replacement text", -1)
|
||||
elif action == "inaccessible":
|
||||
show_inaccessible_window()
|
||||
elif action != "state":
|
||||
raise ValueError("Unknown fixture action")
|
||||
GLib.idle_add(state)
|
||||
return True
|
||||
|
||||
GLib.idle_add(state)
|
||||
GLib.io_add_watch(sys.stdin, GLib.IO_IN | GLib.IO_HUP, command_ready)
|
||||
Gtk.main()
|
||||
if rawClient is not None:
|
||||
rawClient.destroy()
|
||||
connection.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user