def set_unity_property(window): """ Set custom X window property to tell unity 3D this is an on-screen keyboard that wants to be raised on top of dash. See LP 739812, 915250. Since onboard started detecting dash itself this isn't really needed for unity anymore. Leave it anyway, it may come in handy in the future. """ gdk_win = window.get_window() if gdk_win: if hasattr(gdk_win, "get_xid"): # not on wayland xid = gdk_win.get_xid() osk.Util().set_x_property(xid, "ONSCREEN_KEYBOARD", 1)
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): MouseController.__init__(self) self._osk_util = osk.Util() self._click_done_notify_callbacks = [] self._exclusion_rects = []
class LabelPopup(KeyboardPopup): """ Ephemeral popup displaying a key label without user interaction. """ ARROW_HEIGHT = 0.13 ARROW_WIDTH = 0.3 LABEL_MARGIN = 0.1 _pango_layout = None _osk_util = osk.Util() def __init__(self): KeyboardPopup.__init__(self) self._key = None self.connect("realize", self._on_realize_event) self.connect("draw", self._on_draw) def _on_realize_event(self, user_data): self.set_override_redirect(True) # set minimal input shape for the popup to become click-through win = self.get_window() self._osk_util.set_input_rect(win, 0, 0, 1, 1) def _on_draw(self, widget, context): if not LabelPopup._pango_layout: LabelPopup._pango_layout = Pango.Layout( context=Gdk.pango_context_get()) rect = Rect(0, 0, self.get_allocated_width(), self.get_allocated_height()) content_rect = Rect(rect.x, rect.y, rect.w, rect.h - rect.h * self.ARROW_HEIGHT) arrow_rect = Rect(rect.x, content_rect.bottom(), rect.w, rect.h * self.ARROW_HEIGHT) \ .deflate((rect.w - rect.w * self.ARROW_WIDTH) / 2.0, 0) label_rect = content_rect.deflate(rect.w * self.LABEL_MARGIN) # background fill = self._key.get_fill_color() context.save() context.set_operator(cairo.OPERATOR_CLEAR) context.paint() context.restore() context.set_source_rgba(*fill) roundrect_arc(context, content_rect, config.CORNER_RADIUS) context.fill() l, t, r, b = arrow_rect.to_extents() t -= 1 context.move_to(l, t) context.line_to(r, t) context.line_to((l + r) / 2, b) context.fill() # draw label/image label_color = self._key.get_label_color() pixbuf = self._key.get_image(label_rect.w, label_rect.h) if pixbuf: pixbuf.draw(context, label_rect, label_color) else: label = self._key.get_label() if label: if label == " ": label = "␣" self._draw_text(context, label, label_rect, label_color) def _draw_text(self, context, text, rect, rgba): layout = self._pango_layout layout.set_text(text, -1) # find text extents font_description = Pango.FontDescription( \ config.theme_settings.key_label_font) base_extents = self._calc_base_layout_extents(layout, font_description) # scale label to the available rect font_size = self._calc_font_size(rect, base_extents) font_description.set_size(max(1, font_size)) layout.set_font_description(font_description) # center w, h = layout.get_size() w /= Pango.SCALE h /= Pango.SCALE offset = rect.align_rect(Rect(0, 0, w, h)).get_position() # draw context.move_to(*offset) context.set_source_rgba(*rgba) PangoCairo.show_layout(context, layout) @staticmethod def _calc_font_size(rect, base_extents): size_for_maximum_width = rect.w / base_extents[0] size_for_maximum_height = rect.h / base_extents[1] if size_for_maximum_width < size_for_maximum_height: return int(size_for_maximum_width) else: return int(size_for_maximum_height) @staticmethod def _calc_base_layout_extents(layout, font_description): BASE_FONTDESCRIPTION_SIZE = 10000000 font_description.set_size(BASE_FONTDESCRIPTION_SIZE) layout.set_font_description(font_description) w, h = layout.get_size() # In Pango units w = w or 1.0 h = h or 1.0 extents = (w / (Pango.SCALE * BASE_FONTDESCRIPTION_SIZE), h / (Pango.SCALE * BASE_FONTDESCRIPTION_SIZE)) return extents def get_key(self): return self._key def set_key(self, key): self._key = key
class PendingSeparatorPopup(KeyboardPopup): """ Ephemeral popup displaying the pending word separator. """ _osk_util = osk.Util() def __init__(self): KeyboardPopup.__init__(self) self.connect("realize", self._on_realize_event) self._visible = False def show_at(self, view, character_rect): toplevel = view.get_toplevel() self.set_transient_for(toplevel) self.realize() x, y, w, h = character_rect self.set_default_size(w, h) self.resize(w, h) self.move(x, y) self.supports_alpha = view.supports_alpha if self.supports_alpha: self.set_opacity(toplevel.get_opacity()) self.show_all() self._visible = True def hide(self): KeyboardPopup.hide(self) self._visible = False def is_visible(self): return self._visible def _on_realize_event(self, user_data): self.set_override_redirect(True) # set minimal input shape for the popup to become click-through win = self.get_window() self._osk_util.set_input_rect(win, 0, 0, 1, 1) def on_draw(self, widget, cr): rect = Rect(0, 0, self.get_allocated_width(), self.get_allocated_height()) fill_rgba = (1.0, 0.5, 0.5, 0.2) stroke_rgba = (1.0, 0.5, 0.5, 0.7) cr.set_source_rgba(*fill_rgba) cr.rectangle(*rect) cr.fill() r = rect top = r.y + r.h * 0.75 bottom = r.bottom() cr.set_source_rgba(*stroke_rgba) cr.set_line_width(max(1, r.h / 6)) cr.move_to(r.left(), top) cr.line_to(r.left(), bottom) cr.line_to(r.right(), bottom) cr.line_to(r.right(), top) cr.stroke()
def __init__(self, keyboard_widget, icp): _logger.debug("Entered in __init__") self._osk_util = osk.Util() self._osk_struts = osk.Struts() self.application = None self.keyboard_widget = keyboard_widget self.icp = icp self.supports_alpha = False self._visible = False self._sticky = False self._iconified = False self._maximized = False self._screen_resizing = False self._docking_enabled = False self._docking_edge = None self._docking_rect = Rect() self._shrink_work_area = False self._dock_expand = False self._current_struts = None self._monitor_workarea = {} self._opacity = 1.0 self._default_resize_grip = self.get_has_resize_grip() self._override_redirect = False self._type_hint = None self._known_window_rects = [] self._written_window_rects = {} self._written_dock_sizes = {} self._wm_quirks = None self._desktop_switch_count = 0 self._moved_desktop_switch_count = 0 self.set_accept_focus(False) self.set_app_paintable(True) self.set_keep_above(True) #Gtk.Settings.get_default().set_property("gtk-touchscreen-mode", True) Gtk.Window.set_default_icon_name("onboard") self.set_title(_("Onboard")) self.connect("window-state-event", self._cb_window_state_event) self.connect("visibility-notify-event", self._cb_visibility_notify) self.connect('screen-changed', self._cb_screen_changed) self.connect('composited-changed', self._cb_composited_changed) self.connect("realize", self._cb_realize_event) self.connect("unrealize", self._cb_unrealize_event) self.detect_window_manager() self.check_alpha_support() self.update_unrealize_options() self.add(self.keyboard_widget) _logger.debug("Leaving __init__")