#!/usr/bin/python3 import os from gi.repository import Atspi, Gdk, Wnck my_pid = str(os.getpid()) def is_my_app(acc): ''' Based on isMyApp() from https://git.gnome.org/browse/accerciser/tree/src/lib/accerciser/tools.py ''' if not acc: return False else: pid = str(acc.get_application().get_process_id()) if pid == my_pid: return True else: return False def inspect_under_pointer(): ''' Based on _inspectUnderMouse() from https://git.gnome.org/browse/accerciser/tree/plugins/quick_select.py ''' gdk_display = Gdk.Display.open(Gdk.get_display()) screen, x, y, flags = gdk_display.get_pointer() wnck_screen = Wnck.Screen.get_default() wnck_screen.force_update() wnck_workspace = wnck_screen.get_active_workspace() window_order = [w.get_name() for w in wnck_screen.get_windows_stacked()] top_window = (None, -1) desktop_acc = Atspi.get_desktop(0) desktop_acc_child_count = desktop_acc.get_child_count() for desktop_acc_child in range(0, desktop_acc_child_count): app_acc = desktop_acc.get_child_at_index(desktop_acc_child) if not app_acc or is_my_app(app_acc): continue else: app_acc_child_count = app_acc.get_child_count() for app_acc_child in range(0, app_acc_child_count): frame_acc = app_acc.get_child_at_index(app_acc_child) if not frame_acc: continue else: child_acc = get_component_at_coords(frame_acc, x, y) if child_acc: try: z_order = window_order.index(frame_acc.get_name()) except ValueError: pass else: if z_order > top_window[1]: top_window = (child_acc, z_order) if top_window[0]: return top_window[0], x, y def get_component_at_coords(parent, x, y): ''' Based on _getComponentAtCoords() from https://git.gnome.org/browse/accerciser/tree/plugins/quick_select.py ''' container = parent inner_container = None while True: container_role = container.get_role() if container_role == Atspi.Role.PAGE_TAB_LIST: try: si = container.get_selection() container = si.get_selected_child(0) except NotImplementedError: pass try: ci = container.get_component() except: break else: inner_container = container acc_at_point = ci.get_accessible_at_point(x, y, Atspi.CoordType.SCREEN) if acc_at_point is not None: container = acc_at_point else: break if not container or container.get_component() == ci: # The gecko bridge simply has getAccessibleAtPoint return itself # if there are no further children break if inner_container == parent: return None else: return inner_container def get_text_under_pointer(): acc, x, y = inspect_under_pointer() if acc: text = acc.get_text() if text: offset = text.get_offset_at_point(x, y, Atspi.CoordType.SCREEN) text_range = text.get_text_at_offset(offset, Atspi.TextBoundaryType.WORD_START) text_value = Atspi.Text.get_text(text, text_range.start_offset, text_range.end_offset) return text_value return None if __name__ == '__main__': text = get_text_under_pointer() if text: print(text.strip()) else: print('No text found :(')