def __init__(self, wp): self._wp = wp self._accessible = None self._can_insert_text = False self._text_domains = TextDomains() self._text_domain = self._text_domains.get_nop_domain() self._changes = TextChanges() self._entering_text = False self._text_changed = False self._context = "" self._line = "" self._line_caret = 0 self._selection_span = TextSpan() self._begin_of_text = False # context starts at begin of text? self._begin_of_text_offset = None # offset of text begin self._pending_separator_span = None self._last_text_change_time = 0 self._last_caret_move_time = 0 self._last_caret_move_position = 0 self._last_context = None self._last_line = None self._update_context_timer = Timer() self._update_context_delay_normal = 0.01 self._update_context_delay = self._update_context_delay_normal
def on_screen_size_changed(self, screen): """ detect screen rotation (tablets)""" # Give the screen time to settle, the window manager # may block the move to previously invalid positions and # when docked, the slide animation may be drowned out by all # the action in other processes. Timer(1.5, self.on_screen_size_changed_delayed, screen)
def __init__(self): WindowRectTracker.__init__(self) self._screen_orientation = None self._save_position_timer = Timer() # init detection of screen "rotation" screen = self.get_screen() screen.connect('size-changed', self.on_screen_size_changed)
def on_input_sequence_end(self, sequence): key = sequence.active_key if key: keyboard = self.keyboard keyboard.key_up(key, self, sequence) if key and \ not self._drag_selected: Timer(config.UNPRESS_DELAY, self.close_window) else: self.close_window()
def __init__(self, keyboard, notify_done_callback): self._layout = None self._notify_done_callback = notify_done_callback self._drag_selected = False # grazed by the pointer? KeyboardPopup.__init__(self) LayoutView.__init__(self, keyboard) TouchInput.__init__(self) self.connect("destroy", self._on_destroy_event) self._close_timer = Timer() self.start_close_timer()
def __init__(self, redraw_callback, activate_callback): super(ScanMode, self).__init__() logger.debug("ScanMode.__init__()") """ Activation timer instance """ self._activation_timer = Timer() """ Counter for key flash animation """ self._flash = 0 """ Callback for key redraws """ self._redraw_callback = redraw_callback """ Callback for key activation """ self._activate_callback = activate_callback """ A Chunker instance """ self.chunker = None
def init(self): self.keyboard_state = None self._last_mod_mask = 0 self.vk_timer = None self.reset_vk() self._connections = [] self._window = None self.status_icon = None self.service_keyboard = None self._reload_layout_timer = Timer() # finish config initialization config.init() # Optionally wait a little before proceeding. # When the system starts up, the docking strut can be unreliable # on Compiz (Yakkety) and the keyboard may end up at unexpected initial # positions. Delaying the startup when launched by # onboard-autostart.desktop helps to prevent this. delay = config.startup_delay if delay: Timer(delay, self._init_delayed) else: self._init_delayed()
def __init__(self): InputEventSource.__init__(self) self._input_sequences = {} self._touch_events_enabled = False self._multi_touch_enabled = False self._gestures_enabled = False self._last_event_was_touch = False self._last_sequence_time = 0 self._gesture = NO_GESTURE self._gesture_begin_point = (0, 0) self._gesture_begin_time = 0 self._gesture_detected = False self._gesture_cancelled = False self._num_tap_sequences = 0 self._gesture_timer = Timer()
def _on_dwell_begin_timer(self): self._dwell_progress.start_dwelling() self._dwell_timer = Timer(0.025, self._on_dwell_timer) return False
def _start_dwelling(self): self._stop_dwelling() self._dwell_begin_timer = Timer(1.5, self._on_dwell_begin_timer) self._no_more_dwelling = True
def init(self): self.keyboard_state = None self.vk_timer = None self.reset_vk() self._connections = [] self._window = None self.status_icon = None self.service_keyboard = None self._reload_layout_timer = Timer() # finish config initialization config.init() # Release pressed keys when onboard is killed. # Don't keep enter key stuck when being killed by lightdm. self._osk_util = osk.Util() self._osk_util.set_unix_signal_handler(signal.SIGTERM, self.on_sigterm) self._osk_util.set_unix_signal_handler(signal.SIGINT, self.on_sigint) sys.path.append(os.path.join(config.install_dir, 'scripts')) # Create the central keyboard model self.keyboard = Keyboard(self) # Create the initial keyboard widget # Care for toolkit independence only once there is another # supported one besides GTK. self.keyboard_widget = KeyboardWidget(self.keyboard) # create the main window if config.xid_mode: # XEmbed mode for gnome-screensaver? # no icp, don't flash the icon palette in lightdm self._window = KbdPlugWindow(self.keyboard_widget) # write xid to stdout sys.stdout.write('%d\n' % self._window.get_id()) sys.stdout.flush() else: icp = IconPalette(self.keyboard) icp.set_layout_view(self.keyboard_widget) icp.connect("activated", self._on_icon_palette_acticated) self.do_connect(icp.get_menu(), "quit-onboard", lambda x: self.do_quit_onboard()) self._window = KbdWindow(self.keyboard_widget, icp) self.do_connect(self._window, "quit-onboard", lambda x: self.do_quit_onboard()) # config.xid_mode = True self._window.application = self # need this to access screen properties config.main_window = self._window # load the initial layout _logger.info("Loading initial layout") self.reload_layout() # Handle command line options x, y, size after window creation # because the rotation code needs the window's screen. if not config.xid_mode: rect = self._window.get_rect().copy() options = config.options if options.size: size = options.size.split("x") rect.w = int(size[0]) rect.h = int(size[1]) if options.x is not None: rect.x = options.x if options.y is not None: rect.y = options.y # Make sure the keyboard fits on screen rect = self._window.limit_size(rect) if rect != self._window.get_rect(): orientation = self._window.get_screen_orientation() self._window.write_window_rect(orientation, rect) self._window.restore_window_rect() # move/resize early # export dbus service if not config.xid_mode and \ "dbus" in globals(): self.service_keyboard = ServiceOnboardKeyboard(self) # show/hide the window self.keyboard_widget.set_startup_visibility() # keep keyboard window and icon palette on top of dash self._keep_windows_on_top() # connect notifications for keyboard map and group changes self.keymap = Gdk.Keymap.get_default() # map changes self.do_connect(self.keymap, "keys-changed", self.cb_keys_changed) self.do_connect(self.keymap, "state-changed", self.cb_state_changed) # group changes Gdk.event_handler_set(cb_any_event, self) # connect config notifications here to keep config from holding # references to keyboard objects. once = CallOnce(50).enqueue # delay callbacks by 50ms reload_layout = lambda x: once(self.reload_layout_and_present) update_ui = lambda x: once(self._update_ui) update_ui_no_resize = lambda x: once(self._update_ui_no_resize) update_transparency = \ lambda x: once(self.keyboard_widget.update_transparency) update_inactive_transparency = \ lambda x: once(self.keyboard_widget.update_inactive_transparency) # general config.auto_show.enabled_notify_add( lambda x: self.keyboard.update_auto_show()) config.auto_show.hide_on_key_press_notify_add( lambda x: self.keyboard.update_auto_hide()) # keyboard config.keyboard.key_synth_notify_add(reload_layout) config.keyboard.input_event_source_notify_add( lambda x: self.keyboard.update_input_event_source()) config.keyboard.touch_input_notify_add( lambda x: self.keyboard.update_touch_input_mode()) config.keyboard.show_secondary_labels_notify_add(update_ui) # window config.window.window_state_sticky_notify_add( lambda x: self._window.update_sticky_state()) config.window.window_decoration_notify_add( self._on_window_options_changed) config.window.force_to_top_notify_add(self._on_window_options_changed) config.window.keep_aspect_ratio_notify_add(update_ui) config.window.transparency_notify_add(update_transparency) config.window.background_transparency_notify_add(update_transparency) config.window.transparent_background_notify_add(update_ui) config.window.enable_inactive_transparency_notify_add( update_transparency) config.window.inactive_transparency_notify_add( update_inactive_transparency) config.window.docking_notify_add(self._update_docking) # layout config.layout_filename_notify_add(reload_layout) # theme # config.gdi.gtk_theme_notify_add(self.on_gtk_theme_changed) config.theme_notify_add(self.on_theme_changed) config.key_label_font_notify_add(reload_layout) config.key_label_overrides_notify_add(reload_layout) config.theme_settings.color_scheme_filename_notify_add(reload_layout) config.theme_settings.key_label_font_notify_add(reload_layout) config.theme_settings.key_label_overrides_notify_add(reload_layout) config.theme_settings.theme_attributes_notify_add(update_ui) # snippets config.snippets_notify_add(reload_layout) # word suggestions config.word_suggestions.show_context_line_notify_add(update_ui) config.word_suggestions.enabled_notify_add( lambda x: self.keyboard.on_word_suggestions_enabled(x)) config.word_suggestions.auto_learn_notify_add(update_ui_no_resize) config.typing_assistance.active_language_notify_add(lambda x: \ self.keyboard.on_active_lang_id_changed()) config.typing_assistance.spell_check_backend_notify_add(lambda x: \ self.keyboard.on_spell_checker_changed()) config.typing_assistance.auto_capitalization_notify_add(lambda x: \ self.keyboard.on_word_suggestions_enabled(x)) config.word_suggestions.spelling_suggestions_enabled_notify_add(lambda x: \ self.keyboard.on_spell_checker_changed()) config.word_suggestions.delayed_word_separators_enabled_notify_add(lambda x: \ self.keyboard.on_punctuator_changed()) config.word_suggestions.wordlist_buttons_notify_add( update_ui_no_resize) # universal access config.scanner.enabled_notify_add(self.keyboard._on_scanner_enabled) config.window.window_handles_notify_add( self._on_window_handles_changed) # misc config.keyboard.show_click_buttons_notify_add(update_ui) config.lockdown.lockdown_notify_add(update_ui) if config.mousetweaks: config.mousetweaks.state_notify_add(update_ui_no_resize) # create status icon self.status_icon = Indicator() self.status_icon.set_keyboard(self.keyboard) self.do_connect(self.status_icon.get_menu(), "quit-onboard", lambda x: self.do_quit_onboard()) # Callbacks to use when icp or status icon is toggled config.show_status_icon_notify_add(self.show_hide_status_icon) config.icp.in_use_notify_add(self.cb_icp_in_use_toggled) self.show_hide_status_icon(config.show_status_icon) # Minimize to IconPalette if running under GDM if 'RUNNING_UNDER_GDM' in os.environ: _logger.info("RUNNING_UNDER_GDM set, turning on icon palette") config.icp.in_use = True _logger.info("RUNNING_UNDER_GDM set, turning off indicator") config.show_status_icon = False # For some reason the new values don't arrive in gsettings when # running the unit test "test_running_in_live_cd_environment". # -> Force gsettings to apply them, that seems to do the trick. config.icp.apply() config.apply() # unity-2d needs the skip-task-bar hint set before the first mapping. self.show_hide_taskbar() # Check gnome-screen-saver integration # onboard_xembed_enabled False True True True # config.gss.embedded_keyboard_enabled any False any False # config.gss.embedded_keyboard_command any empty !=onboard ==onboard # Action: nop enable Question1 Question2 # silently if not config.xid_mode and \ config.onboard_xembed_enabled: # If it appears, that nothing has touched the gss keys before, # silently enable gss integration with onboard. if not config.gss.embedded_keyboard_enabled and \ not config.gss.embedded_keyboard_command: config.enable_gss_embedding(True) # If onboard is configured to be embedded into the unlock screen # dialog, and the embedding command is different from onboard, ask # the user what to do elif not config.is_onboard_in_xembed_command_string(): question = _( "Onboard is configured to appear with the dialog to " "unlock the screen; for example to dismiss the " "password-protected screensaver.\n\n" "However the system is not configured anymore to use " "Onboard to unlock the screen. A possible reason can " "be that another application configured the system to " "use something else.\n\n" "Would you like to reconfigure the system to show " "Onboard when unlocking the screen?") _logger.warning("showing dialog: '{}'".format(question)) reply = show_confirmation_dialog(question, self._window, config.is_force_to_top()) if reply == True: config.enable_gss_embedding(True) else: config.onboard_xembed_enabled = False else: if not config.gss.embedded_keyboard_enabled: question = _( "Onboard is configured to appear with the dialog " "to unlock the screen; for example to dismiss " "the password-protected screensaver.\n\n" "However this function is disabled in the system.\n\n" "Would you like to activate it?") _logger.warning("showing dialog: '{}'".format(question)) reply = show_confirmation_dialog(question, self._window, config.is_force_to_top()) if reply == True: config.enable_gss_embedding(True) else: config.onboard_xembed_enabled = False # check if gnome accessibility is enabled for auto-show if (config.is_auto_show_enabled() or \ config.are_word_suggestions_enabled()) and \ not config.check_gnome_accessibility(self._window): config.auto_show.enabled = False
class AtspiStateTracker(EventSource): """ Keeps track of the currently active accessible by listening to AT-SPI focus events. """ _focus_event_names = ("text-entry-activated", ) _text_event_names = ("text-changed", "text-caret-moved") _key_stroke_event_names = ("key-pressed", ) _async_event_names = ("async-focus-changed", "async-text-changed", "async-text-caret-moved") _event_names = (_async_event_names + _focus_event_names + _text_event_names + _key_stroke_event_names) _focus_listeners_registered = False _keystroke_listeners_registered = False _text_listeners_registered = False _keystroke_listener = None # asynchronously accessible members _focused_accessible = None # last focused editable accessible _focused_pid = None # pid of last focused editable accessible _active_accessible = None # currently active editable accessible _active_accessible_activation_time = 0.0 # time since focus received _last_active_accessible = None _poll_unity_timer = Timer() def __new__(cls, *args, **kwargs): """ Singleton magic. """ if not hasattr(cls, "self"): cls.self = object.__new__(cls, *args, **kwargs) cls.self.construct() return cls.self def __init__(self): """ Called multiple times, don't use this. """ pass def construct(self): """ Singleton constructor, runs only once. """ EventSource.__init__(self, self._event_names) self._frozen = False def cleanup(self): EventSource.cleanup(self) self._register_atspi_listeners(False) def connect(self, event_name, callback): EventSource.connect(self, event_name, callback) self._update_listeners() def disconnect(self, event_name, callback): had_listeners = self.has_listeners(self._event_names) EventSource.disconnect(self, event_name, callback) self._update_listeners() # help debugging disconnecting events on exit if had_listeners and not self.has_listeners(self._event_names): _logger.info("all listeners disconnected") def _update_listeners(self): register = self.has_listeners(self._focus_event_names) self._register_atspi_focus_listeners(register) register = self.has_listeners(self._text_event_names) self._register_atspi_text_listeners(register) register = self.has_listeners(self._key_stroke_event_names) self._register_atspi_keystroke_listeners(register) def _register_atspi_listeners(self, register): self._register_atspi_focus_listeners(register) self._register_atspi_text_listeners(register) self._register_atspi_keystroke_listeners(register) def _register_atspi_focus_listeners(self, register): if "Atspi" not in globals(): return if self._focus_listeners_registered != register: if register: self.atspi_connect("_listener_focus", "focus", self._on_atspi_global_focus) self.atspi_connect("_listener_object_focus", "object:state-changed:focused", self._on_atspi_object_focus) # private asynchronous events for name in self._async_event_names: handler = "_on_" + name.replace("-", "_") EventSource.connect(self, name, getattr(self, handler)) else: self._poll_unity_timer.stop() self.atspi_disconnect("_listener_focus", "focus") self.atspi_disconnect("_listener_object_focus", "object:state-changed:focused") for name in self._async_event_names: handler = "_on_" + name.replace("-", "_") EventSource.disconnect(self, name, getattr(self, handler)) self._focus_listeners_registered = register def _register_atspi_text_listeners(self, register): if "Atspi" not in globals(): return if self._text_listeners_registered != register: if register: self.atspi_connect("_listener_text_changed", "object:text-changed:insert", self._on_atspi_text_changed) self.atspi_connect("_listener_text_changed", "object:text-changed:delete", self._on_atspi_text_changed) self.atspi_connect("_listener_text_caret_moved", "object:text-caret-moved", self._on_atspi_text_caret_moved) else: self.atspi_disconnect("_listener_text_changed", "object:text-changed:insert") self.atspi_disconnect("_listener_text_changed", "object:text-changed:delete") self.atspi_disconnect("_listener_text_caret_moved", "object:text-caret-moved") self._text_listeners_registered = register def _register_atspi_keystroke_listeners(self, register): if "Atspi" not in globals(): return if self._keystroke_listeners_registered != register: modifier_masks = range(16) if register: if not self._keystroke_listener: self._keystroke_listener = \ Atspi.DeviceListener.new(self._on_atspi_keystroke, None) for modifier_mask in modifier_masks: Atspi.register_keystroke_listener( self._keystroke_listener, None, # key set, None=all modifier_mask, Atspi.KeyEventType.PRESSED, Atspi.KeyListenerSyncType.SYNCHRONOUS) else: # Apparently any single deregister call will turn off # all the other registered modifier_masks too. Since # deregistering takes extremely long (~2.5s for 16 calls) # seize the opportunity and just pick a single arbitrary # mask (Quantal). modifier_masks = [2] for modifier_mask in modifier_masks: Atspi.deregister_keystroke_listener( self._keystroke_listener, None, # key set, None=all modifier_mask, Atspi.KeyEventType.PRESSED) self._keystroke_listeners_registered = register def atspi_connect(self, attribute, event, callback): """ Start listening to an AT-SPI event. Creates a new event listener for each event, since this seems to be the only way to allow reliable deregistering of events. """ if hasattr(self, attribute): listener = getattr(self, attribute) else: listener = None if listener is None: listener = Atspi.EventListener.new(callback, None) setattr(self, attribute, listener) listener.register(event) def atspi_disconnect(self, attribute, event): """ Stop listening to AT-SPI event. """ listener = getattr(self, attribute) listener.deregister(event) def freeze(self): """ Freeze AT-SPI message processing, e.g. while displaying a dialog or popoup menu. """ self._register_atspi_listeners(False) self._frozen = True def thaw(self): """ Resume AT-SPI message processing. """ self._update_listeners() self._frozen = False def emit_async(self, event_name, *args, **kwargs): if not self._frozen: EventSource.emit_async(self, event_name, *args, **kwargs) def _get_cached_accessible(self, accessible): return CachedAccessible(accessible) \ if accessible else None # ######### synchronous handlers ######### # def _on_atspi_global_focus(self, event, user_data): self._on_atspi_focus(event, True) def _on_atspi_object_focus(self, event, user_data): self._on_atspi_focus(event) def _on_atspi_focus(self, event, focus_received=False): focused = (bool(focus_received) or bool(event.detail1)) # received focus? ae = AsyncEvent(accessible=self._get_cached_accessible(event.source), focused=focused) self.emit_async("async-focus-changed", ae) def _on_atspi_text_changed(self, event, user_data): # print("_on_atspi_text_changed", event.detail1, event.detail2, # event.source, event.type, event.type.endswith("delete")) ae = AsyncEvent(accessible=self._get_cached_accessible(event.source), type=event.type, pos=event.detail1, length=event.detail2) self.emit_async("async-text-changed", ae) return False def _on_atspi_text_caret_moved(self, event, user_data): # print("_on_atspi_text_caret_moved", event.detail1, event.detail2, # event.source, event.type, event.source.get_name(), # event.source.get_role()) ae = AsyncEvent(accessible=self._get_cached_accessible(event.source), caret=event.detail1) self.emit_async("async-text-caret-moved", ae) return False def _on_atspi_keystroke(self, event, user_data): if event.type == Atspi.EventType.KEY_PRESSED_EVENT: _logger.atspi("key-stroke {} {} {} {}".format( event.modifiers, event.hw_code, event.id, event.is_text)) # keysym = event.id # What is this? Not an XK_ keysym apparently. ae = AsyncEvent(hw_code=event.hw_code, modifiers=event.modifiers) self.emit_async("key-pressed", ae) return False # don't consume event # ######### asynchronous handlers ######### # def _on_async_focus_changed(self, event): accessible = event.accessible focused = event.focused # Don't access the accessible while frozen. This leads to deadlocks # while displaying Onboard's own dialogs/popup menu's. if self._frozen: return self._log_accessible(accessible, focused) if not accessible: return app_name = accessible.get_app_name().lower() if app_name == "unity": self._handle_focus_changed_unity(event) else: self._handle_focus_changed_apps(event) def _handle_focus_changed_apps(self, event): """ Focus change in regular applications """ accessible = event.accessible focused = event.focused # Since Trusty, focus events no longer come reliably in a # predictable order. -> Store the last editable accessible # so we can pick it over later focused non-editable ones. # Helps to keep the keyboard open in presence of popup selections # e.g. in GNOME's file dialog and in Unity Dash. if self._focused_accessible == accessible: if not focused: self._focused_accessible = None else: pid = accessible.get_pid() if focused: self._poll_unity_timer.stop() if accessible.is_editable(): self._focused_accessible = accessible self._focused_pid = pid # Static accessible, i.e. something that cannot # accidentally steal the focus from an editable # accessible. e.g. firefox ATSPI_ROLE_DOCUMENT_FRAME? elif accessible.is_not_focus_stealing(): self._focused_accessible = None self._focused_pid = None else: # Wily: attempt to hide when unity dash closes # (there's no focus lost event). # Also check duration since last activation to # skip out of order focus events (firefox # ATSPI_ROLE_DOCUMENT_FRAME) for a short while # after opening dash. now = time.time() if focused and \ now - self._active_accessible_activation_time > .5: if self._focused_pid != pid: self._focused_accessible = None _logger.atspi("Dropping accessible due to " "pid change: {} != {} ".format( self._focused_pid, pid)) # Has the previously focused accessible lost the focus? active_accessible = self._focused_accessible if active_accessible and \ not active_accessible.is_focused(True): # Zesty: Firefox 50+ loses focus of the URL entry after # typing just a few letters and focuses a completion # menu item instead. Let's pretend the accessible is # still focused in that case. is_firefox_completion = \ self._focused_accessible.is_urlbar() and \ accessible.get_role() == Atspi.Role.MENU_ITEM if not is_firefox_completion: active_accessible = None self._set_active_accessible(active_accessible) def _handle_focus_changed_unity(self, event): """ Focus change in Unity Dash """ accessible = event.accessible focused = event.focused # Wily: prevent random icons, buttons and toolbars # in unity dash from hiding Onboard. Somehow hovering # over those buttons silently drops the focus from the # text entry. Let's pretend the buttons don't exist # and keep the previously saved text entry active. # Zesty: Don't fight lost focus events anymore, only # react to focus events when the text entry gains focus. if focused and \ accessible.is_editable(): self._focused_accessible = accessible self._set_active_accessible(accessible) # For hiding we poll Dash's toplevel accessible def _poll_unity_dash(): frame = accessible.get_frame() state_set = frame.get_state_set() _logger.debug("polling unity dash state_set: {}".format( AtspiStateType.to_strings(state_set))) if not state_set or \ not state_set.contains(Atspi.StateType.ACTIVE): self._focused_accessible = None self._set_active_accessible(None) return False return True # Only ever start polling if Dash is "ACTIVE". # The state_set might change in the future and the # keyboard better fail to auto-hide than to never show. frame = accessible.get_frame() state_set = frame.get_state_set() _logger.debug("dash focused, state_set: {}".format( AtspiStateType.to_strings(state_set))) if state_set and \ state_set.contains(Atspi.StateType.ACTIVE): self._poll_unity_timer.start(0.5, _poll_unity_dash) def _set_active_accessible(self, accessible): if self._active_accessible != accessible: self._active_accessible = accessible if self._active_accessible or \ self._last_active_accessible: # notify listeners self.emit("text-entry-activated", self._active_accessible) self._last_active_accessible = self._active_accessible self._active_accessible_activation_time = time.time() def _on_async_text_changed(self, event): if event.accessible == self._active_accessible: type = event.type insert = type.endswith(("insert", "insert:system")) delete = type.endswith(("delete", "delete:system")) # print(event.accessible.get_id(), type, insert) if insert or delete: event.insert = insert self.emit("text-changed", event) else: _logger.warning("_on_async_text_changed: " "unknown event type '{}'".format(event.type)) def _on_async_text_caret_moved(self, event): if event.accessible == self._active_accessible: self.emit("text-caret-moved", event) def _log_accessible(self, accessible, focused): if _logger.isEnabledFor(_logger.LEVEL_ATSPI): msg = "AT-SPI focus event: focused={}, ".format(focused) msg += "accessible={}, ".format(accessible) if accessible: name = accessible.get_name() role = accessible.get_role() role_name = accessible.get_role_name() state_set = accessible.get_state_set() states = state_set.states editable = state_set.contains(Atspi.StateType.EDITABLE) \ if state_set else None extents = accessible.get_extents() msg += "name={name}, role={role}({role_name}), " \ "editable={editable}, states={states}, " \ "extents={extents}]" \ .format(accessible=accessible, name=repr(name), role=role.value_name if role else role, role_name=repr(role_name), editable=editable, states=states, extents=extents ) _logger.atspi(msg)