def __init__(self): self._last_ignore_configure_time = None self._last_configures = [] Gtk.Window.__init__(self, urgency_hint = False) WindowRectTracker.__init__(self) self.restore_window_rect(startup = True) self.icp = IconPalette() self.icp.connect("activated", self._on_icon_palette_acticated) self.connect("delete-event", self._on_delete_event) self.connect("configure-event", self._on_configure_event) KbdWindowBase.__init__(self) once = CallOnce(100).enqueue # call at most once per 100ms rect_changed = lambda x: once(self._on_config_rect_changed) config.window.position_notify_add(rect_changed) config.window.size_notify_add(rect_changed)
def __init__(self): gtk.Window.__init__(self) _logger.debug("Entered in __init__") self.keyboard = None self.set_accept_focus(False) self.grab_remove() self.set_keep_above(True) gtk.window_set_default_icon_name("onboard") self.set_title(_("Onboard")) config.geometry_notify_add(self.resize) self.set_default_size(config.keyboard_width, config.keyboard_height) config.position_notify_add(self.move) self.move(config.x_position, config.y_position) self.connect("window-state-event", self.cb_state_change) self.icp = IconPalette() self.icp.connect_object("activated", gtk.Window.deiconify, self) self.show_all() if config.start_minimized: self.iconify() _logger.debug("Leaving __init__")
def _init_delayed(self): # 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 not rect.is_empty() and \ rect != self._window.get_rect(): _logger.debug("limiting window size: {} to {}" .format(self._window.get_rect(), rect)) orientation = self._window.get_screen_orientation() self._window.write_window_rect(orientation, rect) self._window.restore_window_rect() # move/resize early else: _logger.debug("not limiting window size: {} to {}" .format(self._window.get_rect(), rect)) # export dbus service if not config.xid_mode and \ has_dbus: 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 # 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) config.window.docking_aspect_change_range_notify_add( lambda x: self.keyboard_widget .update_docking_aspect_change_range()) # 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) # auto-show 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()) config.auto_show.tablet_mode_detection_notify_add( lambda x: self.keyboard.update_tablet_mode_detection()) config.auto_show.keyboard_device_detection_enabled_notify_add( lambda x: self.keyboard.update_keyboard_device_detection()) # 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
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 KbdWindowBase: """Very messy class holds the keyboard widget. The mess is the docked window support which is disable because of numerous metacity bugs.""" def __init__(self): gtk.Window.__init__(self) _logger.debug("Entered in __init__") self.keyboard = None self.set_accept_focus(False) self.grab_remove() self.set_keep_above(True) gtk.window_set_default_icon_name("onboard") self.set_title(_("Onboard")) config.geometry_notify_add(self.resize) self.set_default_size(config.keyboard_width, config.keyboard_height) config.position_notify_add(self.move) self.move(config.x_position, config.y_position) self.connect("window-state-event", self.cb_state_change) self.icp = IconPalette() self.icp.connect_object("activated", gtk.Window.deiconify, self) self.show_all() if config.start_minimized: self.iconify() _logger.debug("Leaving __init__") def on_deiconify(self, widget=None): self.icp.do_hide() self.move(config.x_position, config.y_position) # to be sure that the window manager places it correctly def on_iconify(self): if config.icp_in_use: self.icp.do_show() def set_keyboard(self, keyboard): _logger.debug("Entered in set_keyboard") if self.keyboard: self.remove(self.keyboard) self.keyboard = keyboard self.add(self.keyboard) self.keyboard.show() self.queue_draw() def do_set_layout(self, client, cxion_id, entry, user_data): _logger.debug("Entered in do_set_layout") return def do_set_gravity(self, edgeGravity): ''' This will place the window on the edge corresponding to the edge gravity ''' _logger.debug("Entered in do_set_gravity") self.edgeGravity = edgeGravity width, height = self.get_size() geom = self.get_screen().get_monitor_geometry(0) eg = self.edgeGravity x = 0 y = 0 if eg == gtk.gdk.GRAVITY_SOUTH: y = geom.height - height y += 29 #to account for panel. self.move(x, y) gobject.idle_add(self.do_set_strut) def do_set_strut(self): _logger.debug("Entered in do_set_strut") propvals = [0,0,0,0,0,0,0,0,0,0,0,0] """propvals = [0,#left 0, #right 0, #top 300,#bottom 0,#left_start_y 0,#left_end_y 0,#right_start_y 0,#right_end_y 0,#top_start_x 0,#top_end_x 0,#bottom_start_x 3000]#bottom_end_x""" screen = self.get_screen() biggestHeight = 0 for n in range(screen.get_n_monitors()): tempHeight = screen.get_monitor_geometry(n).height if biggestHeight < tempHeight: biggestHeight = tempHeight geom = self.get_screen().get_monitor_geometry(0) eg = self.edgeGravity x, y = self.window.get_origin() width,height = self.get_size() if eg == gtk.gdk.GRAVITY_NORTH: propvals[2] = height + y propvals[9] = width elif eg == gtk.gdk.GRAVITY_SOUTH and y != 0: #propvals[2] = y #propvals[9] = geom.width - 1 propvals[3] = biggestHeight - y propvals[11] = width - 1 # tell window manager to not overlap buttons with maximized window self.window.property_change("_NET_WM_STRUT_PARTIAL", "CARDINAL", 32, gtk.gdk.PROP_MODE_REPLACE, propvals) self.queue_resize_no_redraw() def cb_state_change(self, widget, event): """ This is the callback that gets executed when the user hides the onscreen keyboard by using the minimize button in the decoration of the window. """ _logger.debug("Entered in cb_state_change") if event.changed_mask & gtk.gdk.WINDOW_STATE_ICONIFIED: if event.new_window_state & gtk.gdk.WINDOW_STATE_ICONIFIED: self.on_iconify() else: self.on_deiconify() def _hidden(self): return self.window.get_state() & gtk.gdk.WINDOW_STATE_ICONIFIED != 0 hidden = property(_hidden)
class KbdWindow(KbdWindowBase, WindowRectTracker, Gtk.Window): def __init__(self): self._last_ignore_configure_time = None self._last_configures = [] Gtk.Window.__init__(self, urgency_hint = False) WindowRectTracker.__init__(self) self.restore_window_rect(startup = True) self.icp = IconPalette() self.icp.connect("activated", self._on_icon_palette_acticated) self.connect("delete-event", self._on_delete_event) self.connect("configure-event", self._on_configure_event) KbdWindowBase.__init__(self) once = CallOnce(100).enqueue # call at most once per 100ms rect_changed = lambda x: once(self._on_config_rect_changed) config.window.position_notify_add(rect_changed) config.window.size_notify_add(rect_changed) def cleanup(self): WindowRectTracker.cleanup(self) KbdWindowBase.cleanup(self) if self.icp: self.icp.cleanup() self.icp.destroy() self.icp = None def on_visibility_changed(self, visible): if not self._visible and visible: self.move_resize(*self.get_current_rect()) # sync position KbdWindowBase.on_visibility_changed(self, visible) def _on_icon_palette_acticated(self, widget): self.keyboard.toggle_visible() def _on_config_rect_changed(self): """ Gsettings position or size changed """ orientation = self.get_screen_orientation() rect = self.read_window_rect(orientation) # Only apply the new rect if it isn't the one we just wrote to # gsettings. Someone has to have manually changed the values # in gsettings to allow moving the window. if not self.is_known_rect(rect): self.restore_window_rect() def on_user_positioning_begin(self): self.stop_save_position_timer() def on_user_positioning_done(self): self.update_window_rect() self.update_home_rect() def _on_configure_event(self, widget, event): self.update_window_rect() # Configure event due to user positioning? result = self._filter_configure_event(self._window_rect) if result == 0: self.update_home_rect() def _filter_configure_event(self, rect): """ Returns 0 for detected user positioning/sizing. Multiple defenses against false positives, i.e. window movement by autoshow, screen rotation, whathaveyou. """ # There is no user positioning in xembed mode. if config.xid_mode: return -1 # There is no system provided way to move/resize in # force-to-top mode. Solely rely on on_user_positioning_done(). if config.window.force_to_top: return -2 # There is no user positioning for nvisible windows. if not self.is_visible(): return -3 # Remember past n configure events. now = time.time() max_events = 4 self._last_configures = self._last_configures[-(max_events - 1):] # Same rect as before? if len(self._last_configures) and \ self._last_configures[-1][0] == rect: return 1 self._last_configures.append([rect, now]) # Only just started? if len(self._last_configures) < max_events: return 2 # Did we just move the window by auto-show? if not self._last_ignore_configure_time is None and \ time.time() - self._last_ignore_configure_time < 0.5: return 3 # Is the new window rect one of our known ones? if self.is_known_rect(self._window_rect): return 4 # Dragging the decorated frame doesn't produce continous # configure-events anymore as in Oneriric (Precise). # Disable all affected checks based on this. # The home rect will probably get lost occasionally. if not config.has_window_decoration(): # Less than n configure events in the last x seconds? first = self._last_configures[0] intervall = now - first[1] if intervall > 1.0: return 5 # Is there a jump > threshold in past positions? r0 = self._last_configures[-1][0] r1 = self._last_configures[-2][0] dx = r1.x - r0.x dy = r1.y - r0.y d = sqrt(dx * dx + dy * dy) if d > 50: self._last_configures = [] # restart return 6 return 0 def ignore_configure_events(self): self._last_ignore_configure_time = time.time() def remember_rect(self, rect): """ Remember the last 3 rectangles of auto-show repositioning. Time and order of configure events is somewhat unpredictable, so don't rely only on a single remembered rect. """ self._known_window_rects = self._known_window_rects[-2:] self._known_window_rects.append(rect) def get_known_rects(self): """ Return all rects that may have resulted from internal window moves, not by user controlled drag operations. """ rects = self._known_window_rects co = config.window.landscape rects.append(Rect(co.x, co.y, co.width, co.height)) co = config.window.portrait rects.append(Rect(co.x, co.y, co.width, co.height)) rects.append(self.home_rect) return rects def is_known_rect(self, rect): """ The home rect should be updated in response to user positiong/resizing. However we are unable to detect the end of window movement/resizing when window decoration is enabled. Instead we check if the current window rect is different from the ones auto-show knows and assume the user has changed it in this case. """ return any(rect == r for r in self.get_known_rects()) def update_home_rect(self): # update home rect rect = self._window_rect.copy() # Make sure the move button stays visible if self.can_move_into_view(): rect.x, rect.y = self.keyboard.limit_position(rect.x, rect.y) self.home_rect = rect.copy() self.start_save_position_timer() def get_current_rect(self): """ Returns the window rect with auto-show repositioning taken into account. """ if self.keyboard and \ config.is_auto_show_enabled(): rect = self.keyboard.auto_show \ .get_repositioned_window_rect(self.home_rect) if rect: return rect return self.home_rect def on_restore_window_rect(self, rect): """ Overload for WindowRectTracker. """ self.home_rect = rect.copy() # check for alternative auto-show position r = self.get_current_rect() if r != self.home_rect: # remember our rects to distinguish from user move/resize self.remember_rect(r) rect = r return rect def on_save_window_rect(self, rect): """ Overload for WindowRectTracker. """ # Ignore <rect> (self._window_rect), it may just be a temporary one # set by auto-show. Save the user selected home_rect instead. return self.home_rect def read_window_rect(self, orientation): """ Read orientation dependent rect. Overload for WindowRectTracker. """ if orientation == Orientation.LANDSCAPE: co = config.window.landscape else: co = config.window.portrait rect = Rect(co.x, co.y, co.width, co.height) return rect def write_window_rect(self, orientation, rect): """ Write orientation dependent rect. Overload for WindowRectTracker. """ # There are separate rects for normal and rotated screen (tablets). if orientation == Orientation.LANDSCAPE: co = config.window.landscape else: co = config.window.portrait config.settings.delay() co.x, co.y, co.width, co.height = rect config.settings.apply() def _emit_quit_onboard(self, event, data=None): self.emit("quit-onboard") def _on_delete_event(self, event, data=None): if config.lockdown.disable_quit: if self.keyboard: return True else: self._emit_quit_onboard(event)