diff options
51 files changed, 1561 insertions, 633 deletions
diff --git a/UnityCore/GnomeSessionManager.cpp b/UnityCore/GnomeSessionManager.cpp index 466a61125..a4ba1b284 100644 --- a/UnityCore/GnomeSessionManager.cpp +++ b/UnityCore/GnomeSessionManager.cpp @@ -85,6 +85,7 @@ GnomeManager::Impl::Impl(GnomeManager* manager, bool test_mode) , can_hibernate_(false) , pending_action_(shell::Action::NONE) , shell_server_(test_mode_ ? testing::DBUS_NAME : shell::DBUS_NAME) + , open_sessions_(0) { shell_server_.AddObjects(shell::INTROSPECTION_XML, shell::DBUS_OBJECT_PATH); shell_object_ = shell_server_.GetObject(shell::DBUS_INTERFACE); @@ -126,6 +127,22 @@ GnomeManager::Impl::Impl(GnomeManager* manager, bool test_mode) }); } + { + dm_proxy_ = std::make_shared<glib::DBusProxy>("org.freedesktop.DisplayManager", + "/org/freedesktop/DisplayManager", + "org.freedesktop.DisplayManager", + G_BUS_TYPE_SYSTEM); + + dm_proxy_->Connect("SessionAdded", sigc::hide(sigc::mem_fun(this, &Impl::UpdateHaveOtherOpenSessions))); + dm_proxy_->Connect("SessionRemoved", sigc::hide(sigc::mem_fun(this, &Impl::UpdateHaveOtherOpenSessions))); + + UpdateHaveOtherOpenSessions(); + + manager_->have_other_open_sessions.SetGetterFunction([this]() { + return open_sessions_ > 1; + }); + } + CallLogindMethod("CanHibernate", nullptr, [this] (GVariant* variant, glib::Error const& err) { if (err) { @@ -422,6 +439,21 @@ void GnomeManager::Impl::LockScreen(bool prompt) prompt ? manager_->prompt_lock_requested.emit() : manager_->lock_requested.emit(); } +void GnomeManager::Impl::UpdateHaveOtherOpenSessions() +{ + dm_proxy_->GetProperty("Sessions", [this](GVariant* variant) { + GVariantIter *sessions; + g_variant_get(variant, "ao", &sessions); + int open_sessions = g_variant_iter_n_children(sessions); + + if (open_sessions_ != open_sessions) + { + open_sessions_ = open_sessions; + manager_->have_other_open_sessions.changed.emit(open_sessions_); + } + }); +} + // Public implementation GnomeManager::GnomeManager() diff --git a/UnityCore/GnomeSessionManagerImpl.h b/UnityCore/GnomeSessionManagerImpl.h index 1045b78db..1730748c5 100644 --- a/UnityCore/GnomeSessionManagerImpl.h +++ b/UnityCore/GnomeSessionManagerImpl.h @@ -61,6 +61,7 @@ struct GnomeManager::Impl void CallLogindMethod(std::string const& method, GVariant* parameters = nullptr, glib::DBusProxy::CallFinishedCallback const& cb = nullptr); void CallConsoleKitMethod(std::string const& method, GVariant* parameters = nullptr); bool InteractiveMode(); + void UpdateHaveOtherOpenSessions(); GnomeManager* manager_; bool test_mode_; @@ -73,6 +74,9 @@ struct GnomeManager::Impl glib::DBusObject::Ptr shell_object_; glib::DBusProxy::Ptr login_proxy_; glib::DBusProxy::Ptr presence_proxy_; + glib::DBusProxy::Ptr dm_proxy_; + + int open_sessions_; }; } // namespace session diff --git a/UnityCore/SessionManager.h b/UnityCore/SessionManager.h index fb45e5253..2ef619909 100644 --- a/UnityCore/SessionManager.h +++ b/UnityCore/SessionManager.h @@ -23,6 +23,8 @@ #include <sigc++/sigc++.h> #include <memory> +#include <NuxCore/Property.h> + namespace unity { namespace session @@ -36,6 +38,8 @@ public: Manager() = default; virtual ~Manager() = default; + nux::ROProperty<bool> have_other_open_sessions; + virtual std::string RealName() const = 0; virtual std::string UserName() const = 0; virtual std::string HostName() const = 0; diff --git a/dash/DashController.cpp b/dash/DashController.cpp index 0e693c53f..6ecd146cc 100644 --- a/dash/DashController.cpp +++ b/dash/DashController.cpp @@ -66,7 +66,6 @@ Controller::Controller(Controller::WindowCreator const& create_window) , create_window_(create_window) , monitor_(0) , visible_(false) - , need_show_(false) , dbus_server_(dbus::BUS_NAME) , ensure_timeout_(PRELOAD_TIMEOUT_LENGTH) , timeline_animator_(90) @@ -105,7 +104,9 @@ Controller::Controller(Controller::WindowCreator const& create_window) } }); - WindowManager::Default().initiate_spread.connect(sigc::mem_fun(this, &Controller::HideDash)); + auto& wm = WindowManager::Default(); + wm.initiate_spread.connect(sigc::mem_fun(this, &Controller::HideDash)); + wm.screen_viewport_switch_started.connect(sigc::mem_fun(this, &Controller::HideDash)); dbus_server_.AddObjects(dbus::INTROSPECTION, dbus::PATH); dbus_server_.GetObjects().front()->SetMethodsCallsHandler([this] (std::string const& method, GVariant*) { @@ -262,20 +263,14 @@ void Controller::OnMouseDownOutsideWindow(int x, int y, HideDash(); } -void Controller::OnScreenUngrabbed() -{ - LOG_DEBUG(logger) << "On Screen Ungrabbed called"; - if (need_show_) - { - EnsureDash(); - ShowDash(); - } -} - void Controller::OnExternalShowDash(GVariant* variant) { EnsureDash(); - visible_ ? HideDash() : ShowDash(); + + if (!visible_) + ShowDash(); + else + HideDash(); } void Controller::OnExternalHideDash(GVariant* variant) @@ -283,31 +278,44 @@ void Controller::OnExternalHideDash(GVariant* variant) HideDash(); } -void Controller::ShowDash() +bool Controller::ShowDash() { - EnsureDash(); - WindowManager& wm = WindowManager::Default(); // Don't want to show at the wrong time - if (visible_ || wm.IsExpoActive() || wm.IsScaleActive()) - return; + if (visible_) + return false; + + WindowManager& wm = WindowManager::Default(); + + if (wm.IsExpoActive()) + wm.TerminateExpo(); // We often need to wait for the mouse/keyboard to be available while a plugin // is finishing it's animations/cleaning up. In these cases, we patiently wait // for the screen to be available again before honouring the request. if (wm.IsScreenGrabbed()) { - screen_ungrabbed_slot_ = wm.screen_ungrabbed.connect(sigc::mem_fun(this, &Controller::OnScreenUngrabbed)); - need_show_ = true; - return; + screen_ungrabbed_slot_ = wm.screen_ungrabbed.connect([this] { + grab_wait_.reset(); + ShowDash(); + }); + + // Let's wait ungrab event for maximum a couple of seconds... + grab_wait_.reset(new glib::TimeoutSeconds(2, [this] { + screen_ungrabbed_slot_->disconnect(); + return false; + })); + + return false; } + EnsureDash(); monitor_ = GetIdealMonitor(); + screen_ungrabbed_slot_->disconnect(); int launcher_width = unity::Settings::Instance().LauncherWidth(monitor_); view_->SetMonitorOffset(launcher_width, panel::Style::Instance().PanelHeight(monitor_)); view_->AboutToShow(monitor_); FocusWindow(); - need_show_ = false; visible_ = true; StartShowHideTimeline(); @@ -316,6 +324,7 @@ void Controller::ShowDash() GVariant* info = g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "dash", TRUE, monitor_, view_content_geo.width, view_content_geo.height); ubus_manager_.SendMessage(UBUS_OVERLAY_SHOWN, info); + return true; } void Controller::FocusWindow() @@ -347,8 +356,6 @@ void Controller::HideDash() if (!visible_) return; - screen_ungrabbed_slot_->disconnect(); - EnsureDash(); view_->AboutToHide(); @@ -394,7 +401,7 @@ void Controller::OnActivateRequest(GVariant* variant) view_->OnActivateRequest(variant); } -gboolean Controller::CheckShortcutActivation(const char* key_string) +bool Controller::CheckShortcutActivation(const char* key_string) { if (!key_string) return false; diff --git a/dash/DashController.h b/dash/DashController.h index e3256913c..ac5c12d93 100644 --- a/dash/DashController.h +++ b/dash/DashController.h @@ -51,7 +51,7 @@ public: nux::BaseWindow* window() const; - gboolean CheckShortcutActivation(const char* key_string); + bool CheckShortcutActivation(const char* key_string); std::vector<char> GetAllShortcuts(); nux::Property<bool> use_primary; @@ -60,7 +60,7 @@ public: void HideDash(); void QuicklyHideDash(); - void ShowDash(); + bool ShowDash(); void ReFocusKeyInput(); @@ -86,7 +86,6 @@ private: void Relayout(bool check_monitor =false); void OnMouseDownOutsideWindow(int x, int y, unsigned long bflags, unsigned long kflags); - void OnScreenUngrabbed(); void OnExternalShowDash(GVariant* variant); void OnExternalHideDash(GVariant* variant); void OnActivateRequest(GVariant* variant); @@ -103,14 +102,13 @@ private: nux::ObjectPtr<ResizingBaseWindow> window_; nux::ObjectPtr<DashView> view_; int monitor_; - bool visible_; - bool need_show_; connection::Wrapper screen_ungrabbed_slot_; connection::Wrapper form_factor_changed_; glib::DBusServer dbus_server_; glib::TimeoutSeconds ensure_timeout_; + glib::Source::UniquePtr grab_wait_; nux::animation::AnimateValue<double> timeline_animator_; UBusManager ubus_manager_; }; diff --git a/dash/DashView.cpp b/dash/DashView.cpp index a2cc68696..b9e73439a 100644 --- a/dash/DashView.cpp +++ b/dash/DashView.cpp @@ -1457,8 +1457,8 @@ std::vector<char> DashView::GetAllShortcuts() { for (Scope::Ptr scope: scopes_->GetScopes()) { - std::string shortcut = scope->shortcut; - if(shortcut.size() > 0) + std::string const& shortcut = scope->shortcut; + if (!shortcut.empty()) result.push_back(shortcut.at(0)); } } diff --git a/dash/ResultRendererTile.cpp b/dash/ResultRendererTile.cpp index f20a298d0..9b677409e 100644 --- a/dash/ResultRendererTile.cpp +++ b/dash/ResultRendererTile.cpp @@ -424,7 +424,7 @@ void ResultRendererTile::IconLoaded(std::string const& texid, bool IsBlacklistedChar(gunichar uni_c) { if ((uni_c >= 0x1000 && uni_c <= 0x109F) || - (uni_c >= 0xAA60 && uni_c >= 0xAA7B)) + (uni_c >= 0xAA60 && uni_c <= 0xAA7B)) { return true; } diff --git a/debian/changelog b/debian/changelog index 27977c2b6..50538cac3 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,59 @@ +unity (7.2.0+14.10.20140512.4-0ubuntu1) utopic; urgency=low + + [ Stephen M. Webb ] + * lockscreen/LockScreenAccelerators.h: added required header for + std:vector definition + * disabled precompiled headers on arm64 Ubuntu package builds (LP: + #1317276) + + [ Brandon Schaefer ] + * Set the max width, so our static text wraps. So we don't end up + rendering text out of the view area. (LP: #1312749) + + [ Marco Trevisan (Treviño) ] + * UnityScreen: toggle activation of all the unity compiz actions when + locking the screen Also, make sure that the lockscreen views are + treated as "always-on-front" windows by nux (and this applies to + both visibility and events). + * PanelService: emit an invalid EntryActivated signal if the menu + hasn't actually been shown In addition, if the menu is not visible + after the first popup call, try to show a menu without keyboard + focus. This will make possible to see menus also when there's a key- + grab (although they will be without keyboard support). Finally, + reset the menu state, if that failed. + * UnityScreen: always paint the lockscreen above, just add menu and + onboard as exceptions Some code cleanup, factorizing similar code. + (LP: #1313280) + * UnityScreen: don't try to show Dash/Hud if the screen is grabbed + Also move the dash opening out from LauncherController, and get rid + of UBus as first initialization source, as it can only slow things + down here. (LP: #741869) + * PluginAdapter: make sure we don't try to call an invalid + initiate/terminate callback function (LP: #1221673) + * DebugDBusInterface: match properties if they are in the AP array + form [<type>, <value>] (LP: #1307748) + + [ William Hua ] + * Add proper support for modifier-only shortcuts on the lock screen. + (LP: #1291461) + + [ Jinkyu Yi ] + * Dash: Fix problem at checking blacklisted unicode range (LP: + #1251193) + + [ Andrea Azzarone ] + * Add a warning in the session dialog if other sessions are still + open. (LP: #1281058) + + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 12 May 2014 22:03:27 +0000 + +unity (7.2.0+14.04.20140423-0ubuntu2) utopic; urgency=medium + + * debian/control: depends on autopilot-desktop-legacy rather than + autopilot-desktop. + + -- Mathieu Trudel-Lapierre <mathieu-tl@ubuntu.com> Thu, 01 May 2014 00:32:05 -0400 + unity (7.2.0+14.04.20140423-0ubuntu1.2) trusty-security; urgency=medium * SECURITY UPDATE: more lock screen bypass issues, and regression with diff --git a/debian/control b/debian/control index 0609086f2..214cc81dd 100644 --- a/debian/control +++ b/debian/control @@ -165,7 +165,7 @@ Section: python Architecture: all Depends: ${misc:Depends}, ${python:Depends}, - autopilot-desktop, + autopilot-desktop-legacy, python-windowmocker, gir1.2-appindicator3-0.1, gir1.2-dee-1.0, diff --git a/debian/rules b/debian/rules index 1e862b9df..d6ef3b2f6 100755 --- a/debian/rules +++ b/debian/rules @@ -23,14 +23,18 @@ LIBUNITY_PRIVATE := $(shell pkg-config --libs-only-L unity-protocol-private | se SCOPES_RECOMMENDS := $(shell perl debian/scopes-recommends-generator /usr/share/unity/client-scopes.json) - -override_dh_auto_configure: +cmake_base_options := -DUSE_GSETTINGS=TRUE -DCOMPIZ_BUILD_WITH_RPATH=FALSE -DCOMPIZ_PACKAGING_ENABLED=TRUE -DCOMPIZ_PLUGIN_INSTALL_TYPE=package ifeq ($(DEB_HOST_ARCH),$(findstring $(DEB_HOST_ARCH), $(gles2_architectures))) - dh_auto_configure -- -DUSE_GSETTINGS=TRUE -DCOMPIZ_BUILD_WITH_RPATH=FALSE -DCOMPIZ_PACKAGING_ENABLED=TRUE -DCOMPIZ_PLUGIN_INSTALL_TYPE=package -DBUILD_GLES=TRUE -DDISABLE_MAINTAINER_CFLAGS=ON -else - dh_auto_configure -- -DUSE_GSETTINGS=TRUE -DCOMPIZ_BUILD_WITH_RPATH=FALSE -DCOMPIZ_PACKAGING_ENABLED=TRUE -DCOMPIZ_PLUGIN_INSTALL_TYPE=package + cmake_gl_options := -DBUILD_GLES=TRUE -DDISABLE_MAINTAINER_CFLAGS=ON +endif +ifeq ($(DEB_HOST_ARCH),$(findstring $(DEB_HOST_ARCH), arm64)) + cmake_pch_options := -Duse_pch=OFF endif + +override_dh_auto_configure: + dh_auto_configure -- $(cmake_base_options) $(cmake_gl_options) $(cmake_pch_options) + override_dh_install: # install autopilot tests cd tests/autopilot; \ diff --git a/hud/HudController.cpp b/hud/HudController.cpp index 58134d4a6..aefe40f63 100644 --- a/hud/HudController.cpp +++ b/hud/HudController.cpp @@ -102,6 +102,7 @@ Controller::Controller(Controller::ViewCreator const& create_view, WindowManager& wm = WindowManager::Default(); wm.screen_ungrabbed.connect(sigc::mem_fun(this, &Controller::OnScreenUngrabbed)); wm.initiate_spread.connect(sigc::mem_fun(this, &Controller::HideHud)); + wm.screen_viewport_switch_started.connect(sigc::mem_fun(this, &Controller::HideHud)); hud_service_.queries_updated.connect(sigc::mem_fun(this, &Controller::OnQueriesFinished)); timeline_animator_.updated.connect(sigc::mem_fun(this, &Controller::OnViewShowHideFrame)); diff --git a/launcher/AbstractLauncherIcon.h b/launcher/AbstractLauncherIcon.h index fe2600062..5cacbe96d 100644 --- a/launcher/AbstractLauncherIcon.h +++ b/launcher/AbstractLauncherIcon.h @@ -110,10 +110,11 @@ public: UNFOLDED, STARTING, SHIMMER, - CENTER_SAVED, - PROGRESS, DESAT, + GLOW, + PROGRESS, PULSE_ONCE, + CENTER_SAVED, LAST }; diff --git a/launcher/ApplicationLauncherIcon.cpp b/launcher/ApplicationLauncherIcon.cpp index d88fe3e1f..4d079f1fd 100644 --- a/launcher/ApplicationLauncherIcon.cpp +++ b/launcher/ApplicationLauncherIcon.cpp @@ -49,9 +49,10 @@ namespace // We use the "bamf-" prefix since the manager is protected, to avoid name clash const std::string WINDOW_MOVE_TIMEOUT = "bamf-window-move"; const std::string ICON_REMOVE_TIMEOUT = "bamf-icon-remove"; -//const std::string ICON_DND_OVER_TIMEOUT = "bamf-icon-dnd-over"; +const std::string ICON_DND_OVER_TIMEOUT = "bamf-icon-dnd-over"; const std::string DEFAULT_ICON = "application-default-icon"; const int MAXIMUM_QUICKLIST_WIDTH = 300; +const int COMPIZ_SCALE_DND_SPREAD = 1 << 7; enum MenuItemType { @@ -1157,28 +1158,28 @@ std::string ApplicationLauncherIcon::GetRemoteUri() const return _remote_uri; } -void ApplicationLauncherIcon::OnDndHovered() -{ - // for now, let's not do this, it turns out to be quite buggy - //if (IsRunning()) - // Spread(CompAction::StateInitEdgeDnd, true); -} - void ApplicationLauncherIcon::OnDndEnter() { - /* Disabled, since the DND code is currently disabled as well. - _source_manager.AddTimeout(1000, [this] { - OnDndHovered(); + auto timestamp = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp; + + _source_manager.AddTimeout(1000, [this, timestamp] { + WindowManager::Default().TerminateScale(); + + if (!IsRunning()) + return false; + + Focus(ActionArg(ActionArg::Source::LAUNCHER, 1, timestamp)); + + if (GetWindows(WindowFilter::ON_CURRENT_DESKTOP).size() > 1) + Spread(true, COMPIZ_SCALE_DND_SPREAD, false); + return false; }, ICON_DND_OVER_TIMEOUT); - */ } void ApplicationLauncherIcon::OnDndLeave() { - /* Disabled, since the DND code is currently disabled as well. _source_manager.Remove(ICON_DND_OVER_TIMEOUT); - */ } bool ApplicationLauncherIcon::IsFileManager() diff --git a/launcher/ApplicationLauncherIcon.h b/launcher/ApplicationLauncherIcon.h index e7a26fed7..b92ad5e1f 100644 --- a/launcher/ApplicationLauncherIcon.h +++ b/launcher/ApplicationLauncherIcon.h @@ -83,7 +83,6 @@ protected: void AddProperties(debug::IntrospectionData&); void OnAcceptDrop(DndData const& dnd_data); void OnDndEnter(); - void OnDndHovered(); void OnDndLeave(); void OpenInstanceLauncherIcon(Time timestamp) override; void ToggleSticky(); diff --git a/launcher/Launcher.cpp b/launcher/Launcher.cpp index 4b67ddc30..088e2b168 100644 --- a/launcher/Launcher.cpp +++ b/launcher/Launcher.cpp @@ -157,6 +157,7 @@ Launcher::Launcher(MockableBaseWindow* parent, hide_machine_.should_hide_changed.connect(sigc::mem_fun(this, &Launcher::SetHidden)); hide_machine_.reveal_progress.changed.connect(redraw_cb); hover_machine_.should_hover_changed.connect(sigc::mem_fun(this, &Launcher::SetHover)); + bg_effect_helper_.enabled.changed.connect(redraw_cb); mouse_down.connect(sigc::mem_fun(this, &Launcher::RecvMouseDown)); mouse_up.connect(sigc::mem_fun(this, &Launcher::RecvMouseUp)); @@ -599,7 +600,7 @@ void Launcher::SetupRenderArg(AbstractLauncherIcon::Ptr const& icon, RenderArg& else urgent_progress = CLAMP(urgent_progress * 3.0f - 2.0f, 0.0f, 1.0f); // we want to go 3x faster than the urgent normal cycle - arg.glow_intensity = urgent_progress; + arg.glow_intensity = icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::GLOW, monitor()) + urgent_progress; if (options()->urgent_animation() == URGENT_ANIMATION_WIGGLE) { @@ -809,13 +810,11 @@ void Launcher::RenderArgs(std::list<RenderArg> &launcher_args, } } - float drag_hide_progress = dnd_hide_animation_.GetCurrentValue(); - if (options()->hide_mode != LAUNCHER_HIDE_NEVER && drag_hide_progress > 0.0f) + if (options()->hide_mode != LAUNCHER_HIDE_NEVER) { + float drag_hide_progress = dnd_hide_animation_.GetCurrentValue(); autohide_offset -= geo.width * 0.25f * drag_hide_progress; - - if (drag_hide_progress >= 1.0f) - hide_machine_.SetQuirk(LauncherHideMachine::DND_PUSHED_OFF, true); + hide_machine_.SetQuirk(LauncherHideMachine::DND_PUSHED_OFF, (drag_hide_progress >= 1.0f)); } // Inform the painter where to paint the box @@ -1171,6 +1170,9 @@ void Launcher::OnSpreadChanged() hide_machine_.SetQuirk(LauncherHideMachine::SCALE_ACTIVE, active); bg_effect_helper_.enabled = active; + if (hide_machine_.GetQuirk(LauncherHideMachine::EXTERNAL_DND_ACTIVE)) + return; + if (active && icon_under_mouse_) icon_under_mouse_->HideTooltip(); @@ -1592,6 +1594,7 @@ void Launcher::SetupIconAnimations(AbstractLauncherIcon::Ptr const& icon) icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::CENTER_SAVED, ANIM_DURATION, monitor()); icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::PROGRESS, ANIM_DURATION, monitor()); icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::DESAT, ANIM_DURATION_SHORT_SHORT, monitor()); + icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::GLOW, ANIM_DURATION_SHORT, monitor()); if (options()->urgent_animation() == URGENT_ANIMATION_WIGGLE) icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::URGENT, (ANIM_DURATION_SHORT * WIGGLE_CYCLES), monitor()); @@ -2523,7 +2526,10 @@ void Launcher::DndHoveredIconReset() } if (!steal_drag_ && dnd_hovered_icon_) + { dnd_hovered_icon_->SendDndLeave(); + dnd_hovered_icon_->SetQuirk(AbstractLauncherIcon::Quirk::GLOW, false, monitor()); + } steal_drag_ = false; drag_edge_touching_ = false; @@ -2535,7 +2541,6 @@ void Launcher::ProcessDndLeave() { #ifdef USE_X11 SetStateMouseOverLauncher(false); - DndHoveredIconReset(); #endif } @@ -2581,7 +2586,10 @@ void Launcher::ProcessDndMove(int x, int y, std::list<char*> mimes) mouse_position_.y <= (parent_->GetGeometry().height - icon_size_.CP(cv_) - 2 * SPACE_BETWEEN_ICONS.CP(cv_))) { if (dnd_hovered_icon_) - dnd_hovered_icon_->SendDndLeave(); + { + dnd_hovered_icon_->SendDndLeave(); + dnd_hovered_icon_->SetQuirk(AbstractLauncherIcon::Quirk::GLOW, false, monitor()); + } animation::StartOrReverse(dnd_hide_animation_, animation::Direction::FORWARD); drag_edge_touching_ = true; @@ -2640,6 +2648,9 @@ void Launcher::ProcessDndMove(int x, int y, std::list<char*> mimes) { hovered_icon->SendDndEnter(); drag_action_ = hovered_icon->QueryAcceptDrop(dnd_data_); + + if (drag_action_ != nux::DNDACTION_NONE) + hovered_icon->SetQuirk(AbstractLauncherIcon::Quirk::GLOW, true, monitor()); } else { @@ -2647,7 +2658,10 @@ void Launcher::ProcessDndMove(int x, int y, std::list<char*> mimes) } if (dnd_hovered_icon_) + { dnd_hovered_icon_->SendDndLeave(); + dnd_hovered_icon_->SetQuirk(AbstractLauncherIcon::Quirk::GLOW, false, monitor()); + } dnd_hovered_icon_ = hovered_icon; } @@ -2790,8 +2804,10 @@ void Launcher::UnsetDndQuirk() } } + + hide_machine_.SetQuirk(LauncherHideMachine::MT_DRAG_OUT, drag_out_delta_x_ >= DRAG_OUT_PIXELS - 90.0f); hide_machine_.SetQuirk(LauncherHideMachine::EXTERNAL_DND_ACTIVE, false); - hide_machine_.SetQuirk(LauncherHideMachine::EXTERNAL_DND_ACTIVE, false); + animation::SetValue(dnd_hide_animation_, animation::Direction::BACKWARD); #endif } diff --git a/launcher/LauncherController.cpp b/launcher/LauncherController.cpp index 86d8ba20c..39b54d807 100644 --- a/launcher/LauncherController.cpp +++ b/launcher/LauncherController.cpp @@ -1199,18 +1199,6 @@ bool Controller::AboutToShowDash(int was_tap, int when) const void Controller::HandleLauncherKeyRelease(bool was_tap, int when) { int tap_duration = when - pimpl->launcher_key_press_time_; - WindowManager& wm = WindowManager::Default(); - - if (tap_duration < options()->super_tap_duration && was_tap && - !wm.IsTopWindowFullscreenOnMonitorWithMouse()) - { - LOG_DEBUG(logger) << "Quick tap, sending activation request."; - pimpl->SendHomeActivationRequest(); - } - else - { - LOG_DEBUG(logger) << "Tap too long: " << tap_duration; - } pimpl->sources_.Remove(local::LABELS_TIMEOUT); pimpl->sources_.Remove(local::KEYPRESS_TIMEOUT); diff --git a/launcher/TrashLauncherIcon.cpp b/launcher/TrashLauncherIcon.cpp index 8c254e6de..fdc540e43 100644 --- a/launcher/TrashLauncherIcon.cpp +++ b/launcher/TrashLauncherIcon.cpp @@ -173,6 +173,7 @@ void TrashLauncherIcon::OnAcceptDrop(DndData const& dnd_data) } SetQuirk(LauncherIcon::Quirk::PULSE_ONCE, true); + FullyAnimateQuirkDelayed(100, LauncherIcon::Quirk::SHIMMER); } std::string TrashLauncherIcon::GetName() const diff --git a/launcher/VolumeLauncherIcon.cpp b/launcher/VolumeLauncherIcon.cpp index 0818cf7c1..0cec6afeb 100644 --- a/launcher/VolumeLauncherIcon.cpp +++ b/launcher/VolumeLauncherIcon.cpp @@ -410,6 +410,7 @@ void VolumeLauncherIcon::OnAcceptDrop(DndData const& dnd_data) auto timestamp = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp; pimpl_->CopyFilesToVolume(dnd_data.Uris(), timestamp); SetQuirk(Quirk::PULSE_ONCE, true); + FullyAnimateQuirkDelayed(100, LauncherIcon::Quirk::SHIMMER); } // diff --git a/lockscreen/CMakeLists.txt b/lockscreen/CMakeLists.txt index b32421f8f..47c1c54ed 100644 --- a/lockscreen/CMakeLists.txt +++ b/lockscreen/CMakeLists.txt @@ -24,6 +24,8 @@ set (LOCKSCREEN_SOURCES LockScreenShield.cpp LockScreenShieldFactory.cpp LockScreenPanel.cpp + LockScreenAcceleratorController.cpp + LockScreenAccelerators.cpp ScreenSaverDBusManager.cpp UserAuthenticatorPam.cpp UserPromptView.cpp diff --git a/lockscreen/LockScreenAbstractShield.h b/lockscreen/LockScreenAbstractShield.h index 52f063e84..d974f1f5c 100644 --- a/lockscreen/LockScreenAbstractShield.h +++ b/lockscreen/LockScreenAbstractShield.h @@ -25,6 +25,7 @@ #include <UnityCore/Indicators.h> #include "unity-shared/MockableBaseWindow.h" +#include "LockScreenAccelerators.h" namespace unity { @@ -34,12 +35,13 @@ namespace lockscreen class AbstractShield : public MockableBaseWindow { public: - AbstractShield(session::Manager::Ptr const& session, indicator::Indicators::Ptr const& indicators, int monitor_num, bool is_primary) + AbstractShield(session::Manager::Ptr const& session, indicator::Indicators::Ptr const& indicators, Accelerators::Ptr const& accelerators, int monitor_num, bool is_primary) : MockableBaseWindow("Unity Lockscreen") , primary(is_primary) , monitor(monitor_num) , session_manager_(session) , indicators_(indicators) + , accelerators_(accelerators) {} nux::Property<bool> primary; @@ -47,7 +49,7 @@ public: using MockableBaseWindow::RemoveLayout; virtual bool IsIndicatorOpen() const = 0; - virtual void CheckCapsLockPrompt() = 0; + virtual void ActivatePanel() = 0; sigc::signal<void, int, int> grab_motion; sigc::signal<void, unsigned long, unsigned long> grab_key; @@ -55,6 +57,7 @@ public: protected: session::Manager::Ptr session_manager_; indicator::Indicators::Ptr indicators_; + Accelerators::Ptr accelerators_; }; } // lockscreen diff --git a/lockscreen/LockScreenAcceleratorController.cpp b/lockscreen/LockScreenAcceleratorController.cpp new file mode 100644 index 000000000..298d8def9 --- /dev/null +++ b/lockscreen/LockScreenAcceleratorController.cpp @@ -0,0 +1,146 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* + * Copyright (C) 2014 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authored by: William Hua <william.hua@canonical.com> + */ + +#include "LockScreenAcceleratorController.h" + +#include <UnityCore/GLibDBusProxy.h> + +namespace unity +{ +namespace lockscreen +{ + +namespace +{ +const char* const MEDIA_KEYS_SCHEMA = "org.gnome.settings-daemon.plugins.media-keys"; +const char* const MEDIA_KEYS_KEY_VOLUME_MUTE = "volume-mute"; +const char* const MEDIA_KEYS_KEY_VOLUME_DOWN = "volume-down"; +const char* const MEDIA_KEYS_KEY_VOLUME_UP = "volume-up"; +const char* const INPUT_SWITCH_SCHEMA = "org.gnome.desktop.wm.keybindings"; +const char* const INPUT_SWITCH_KEY_PREVIOUS_SOURCE = "switch-input-source-backward"; +const char* const INPUT_SWITCH_KEY_NEXT_SOURCE = "switch-input-source"; + +const char* const INDICATOR_INTERFACE_ACTIONS = "org.gtk.Actions"; +const char* const INDICATOR_METHOD_ACTIVATE = "Activate"; +const char* const INDICATOR_SOUND_BUS_NAME = "com.canonical.indicator.sound"; +const char* const INDICATOR_SOUND_OBJECT_PATH = "/com/canonical/indicator/sound"; +const char* const INDICATOR_SOUND_ACTION_MUTE = "mute"; +const char* const INDICATOR_SOUND_ACTION_SCROLL = "scroll"; +const char* const INDICATOR_KEYBOARD_BUS_NAME = "com.canonical.indicator.keyboard"; +const char* const INDICATOR_KEYBOARD_OBJECT_PATH = "/com/canonical/indicator/keyboard"; +const char* const INDICATOR_KEYBOARD_ACTION_SCROLL = "locked_scroll"; + +void ActivateIndicator(std::string const& bus_name, + std::string const& object_path, + std::string const& action_name, + glib::Variant const& parameters = glib::Variant()) +{ + GVariantBuilder builder; + + g_variant_builder_init(&builder, G_VARIANT_TYPE("(sava{sv})")); + g_variant_builder_add(&builder, "s", action_name.c_str()); + + if (parameters) + g_variant_builder_add_parsed(&builder, "[%v]", static_cast<GVariant*>(parameters)); + else + g_variant_builder_add_parsed(&builder, "@av []"); + + g_variant_builder_add_parsed(&builder, "@a{sv} []"); + + auto proxy = std::make_shared<glib::DBusProxy>(bus_name, object_path, INDICATOR_INTERFACE_ACTIONS); + proxy->CallBegin(INDICATOR_METHOD_ACTIVATE, g_variant_builder_end(&builder), [proxy] (GVariant*, glib::Error const&) {}); +} + +void MuteIndicatorSound() +{ + ActivateIndicator(INDICATOR_SOUND_BUS_NAME, + INDICATOR_SOUND_OBJECT_PATH, + INDICATOR_SOUND_ACTION_MUTE); +} + +void ScrollIndicatorSound(int offset) +{ + ActivateIndicator(INDICATOR_SOUND_BUS_NAME, + INDICATOR_SOUND_OBJECT_PATH, + INDICATOR_SOUND_ACTION_SCROLL, + g_variant_new_int32(offset)); +} + +void ScrollIndicatorKeyboard(int offset) +{ + ActivateIndicator(INDICATOR_KEYBOARD_BUS_NAME, + INDICATOR_KEYBOARD_OBJECT_PATH, + INDICATOR_KEYBOARD_ACTION_SCROLL, + g_variant_new_int32(-offset)); +} +} // namespace + +AcceleratorController::AcceleratorController() + : accelerators_(new Accelerators) +{ + auto settings = glib::Object<GSettings>(g_settings_new(MEDIA_KEYS_SCHEMA)); + + auto accelerator = std::make_shared<Accelerator>(glib::String(g_settings_get_string(settings, MEDIA_KEYS_KEY_VOLUME_MUTE))); + accelerator->activated.connect(std::function<void ()>(MuteIndicatorSound)); + accelerators_->Add(accelerator); + + accelerator = std::make_shared<Accelerator>(glib::String(g_settings_get_string(settings, MEDIA_KEYS_KEY_VOLUME_DOWN))); + accelerator->activated.connect(std::bind(ScrollIndicatorSound, -1)); + accelerators_->Add(accelerator); + + accelerator = std::make_shared<Accelerator>(glib::String(g_settings_get_string(settings, MEDIA_KEYS_KEY_VOLUME_UP))); + accelerator->activated.connect(std::bind(ScrollIndicatorSound, +1)); + accelerators_->Add(accelerator); + + settings = glib::Object<GSettings>(g_settings_new(INPUT_SWITCH_SCHEMA)); + + auto variant = glib::Variant(g_settings_get_value(settings, INPUT_SWITCH_KEY_PREVIOUS_SOURCE), glib::StealRef()); + + if (g_variant_n_children(variant) > 0) + { + const gchar* string; + + g_variant_get_child(variant, 0, "&s", &string); + + accelerator = std::make_shared<Accelerator>(string); + accelerator->activated.connect(std::bind(ScrollIndicatorKeyboard, -1)); + accelerators_->Add(accelerator); + } + + variant = glib::Variant(g_settings_get_value(settings, INPUT_SWITCH_KEY_NEXT_SOURCE), glib::StealRef()); + + if (g_variant_n_children(variant) > 0) + { + const gchar* string; + + g_variant_get_child(variant, 0, "&s", &string); + + accelerator = std::make_shared<Accelerator>(string); + accelerator->activated.connect(std::bind(ScrollIndicatorKeyboard, +1)); + accelerators_->Add(accelerator); + } +} + +Accelerators::Ptr const& AcceleratorController::GetAccelerators() const +{ + return accelerators_; +} + +} // lockscreen namespace +} // unity namespace diff --git a/lockscreen/LockScreenAcceleratorController.h b/lockscreen/LockScreenAcceleratorController.h new file mode 100644 index 000000000..a69b245eb --- /dev/null +++ b/lockscreen/LockScreenAcceleratorController.h @@ -0,0 +1,46 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* + * Copyright (C) 2014 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authored by: William Hua <william.hua@canonical.com> + */ + +#ifndef UNITY_LOCKSCREEN_ACCELERATOR_CONTROLLER +#define UNITY_LOCKSCREEN_ACCELERATOR_CONTROLLER + +#include "LockScreenAccelerators.h" + +namespace unity +{ +namespace lockscreen +{ + +class AcceleratorController +{ +public: + typedef std::shared_ptr<AcceleratorController> Ptr; + + AcceleratorController(); + + Accelerators::Ptr const& GetAccelerators() const; + +private: + Accelerators::Ptr accelerators_; +}; + +} // lockscreen namespace +} // unity namespace + +#endif // UNITY_LOCKSCREEN_ACCELERATOR_CONTROLLER diff --git a/lockscreen/LockScreenAccelerators.cpp b/lockscreen/LockScreenAccelerators.cpp new file mode 100644 index 000000000..f3f4a4bc7 --- /dev/null +++ b/lockscreen/LockScreenAccelerators.cpp @@ -0,0 +1,528 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* + * Copyright (C) 2014 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authored by: William Hua <william.hua@canonical.com> + */ + +#include "LockScreenAccelerators.h" + +#include <NuxGraphics/Events.h> +#include <gtk/gtk.h> + +namespace unity +{ +namespace lockscreen +{ + +enum class PressedState : unsigned int +{ + NothingPressed = 0x00, + LeftShiftPressed = 0x01, + LeftControlPressed = 0x02, + LeftAltPressed = 0x04, + LeftSuperPressed = 0x08, + RightShiftPressed = 0x10, + RightControlPressed = 0x20, + RightAltPressed = 0x40, + RightSuperPressed = 0x80 +}; + +PressedState operator~(PressedState const& first) +{ + return static_cast<PressedState>(~static_cast<unsigned int>(first)); +} + +PressedState operator&(PressedState const& first, PressedState const& second) +{ + return static_cast<PressedState>(static_cast<unsigned int>(first) & static_cast<unsigned int>(second)); +} + +PressedState operator|(PressedState const& first, PressedState const& second) +{ + return static_cast<PressedState>(static_cast<unsigned int>(first) | static_cast<unsigned int>(second)); +} + +PressedState& operator&=(PressedState& first, PressedState const& second) +{ + return first = first & second; +} + +PressedState& operator|=(PressedState& first, PressedState const& second) +{ + return first = first | second; +} + +namespace +{ +unsigned int KeysymToModifier(unsigned int keysym) +{ + switch (keysym) + { + case GDK_KEY_Shift_L: + case GDK_KEY_Shift_R: + return nux::KEY_MODIFIER_SHIFT; + case GDK_KEY_Control_L: + case GDK_KEY_Control_R: + return nux::KEY_MODIFIER_CTRL; + case GDK_KEY_Meta_L: + case GDK_KEY_Meta_R: + case GDK_KEY_Alt_L: + case GDK_KEY_Alt_R: + return nux::KEY_MODIFIER_ALT; + case GDK_KEY_Super_L: + case GDK_KEY_Super_R: + return nux::KEY_MODIFIER_SUPER; + } + + return 0; +} + +PressedState KeysymToPressedState(unsigned int keysym) +{ + switch (keysym) + { + case GDK_KEY_Shift_L: + return PressedState::LeftShiftPressed; + case GDK_KEY_Shift_R: + return PressedState::RightShiftPressed; + case GDK_KEY_Control_L: + return PressedState::LeftControlPressed; + case GDK_KEY_Control_R: + return PressedState::RightControlPressed; + case GDK_KEY_Meta_L: + case GDK_KEY_Alt_L: + return PressedState::LeftAltPressed; + case GDK_KEY_Meta_R: + case GDK_KEY_Alt_R: + return PressedState::RightAltPressed; + case GDK_KEY_Super_L: + return PressedState::LeftSuperPressed; + case GDK_KEY_Super_R: + return PressedState::RightSuperPressed; + } + + return PressedState::NothingPressed; +} + +unsigned int KeysymToMirrorKeysym(unsigned int keysym) +{ + switch (keysym) + { + case GDK_KEY_Shift_L: + return GDK_KEY_Shift_R; + case GDK_KEY_Shift_R: + return GDK_KEY_Shift_L; + case GDK_KEY_Control_L: + return GDK_KEY_Control_R; + case GDK_KEY_Control_R: + return GDK_KEY_Control_L; + case GDK_KEY_Meta_L: + return GDK_KEY_Meta_R; + case GDK_KEY_Meta_R: + return GDK_KEY_Meta_L; + case GDK_KEY_Alt_L: + return GDK_KEY_Alt_R; + case GDK_KEY_Alt_R: + return GDK_KEY_Alt_L; + case GDK_KEY_Super_L: + return GDK_KEY_Super_R; + case GDK_KEY_Super_R: + return GDK_KEY_Super_L; + } + + return 0; +} +} // namespace + +Accelerator::Accelerator(unsigned int keysym, + unsigned int keycode, + unsigned int modifiers) + : keysym_(keysym) + , keycode_(keycode) + , modifiers_(modifiers) + , active_(true) + , activated_(false) +{ +} + +Accelerator::Accelerator(std::string const& string) + : keysym_(0) + , keycode_(0) + , modifiers_(0) + , active_(true) + , activated_(false) +{ + guint keysym; + guint* keycodes; + GdkModifierType modifiers; + + gtk_accelerator_parse_with_keycode(string.c_str(), &keysym, &keycodes, &modifiers); + + /* gtk_accelerator_parse_with_keycode() might fail if the key is not in the + * default key map. gtk_accelerator_parse() might succeed in this case. */ + if (keysym == 0 && keycodes == NULL && modifiers == 0) + gtk_accelerator_parse(string.c_str(), &keysym, &modifiers); + + keysym_ = keysym; + + if (keycodes != NULL) + { + keycode_ = keycodes[0]; + g_free(keycodes); + } + + if (modifiers & GDK_SHIFT_MASK) + modifiers_ |= nux::KEY_MODIFIER_SHIFT; + if (modifiers & GDK_CONTROL_MASK) + modifiers_ |= nux::KEY_MODIFIER_CTRL; + if ((modifiers & GDK_MOD1_MASK) || (modifiers & GDK_META_MASK)) + modifiers_ |= nux::KEY_MODIFIER_ALT; + if (modifiers & GDK_SUPER_MASK) + modifiers_ |= nux::KEY_MODIFIER_SUPER; +} + +bool Accelerator::operator==(Accelerator const& accelerator) const +{ + return keysym_ == accelerator.keysym_ + && keycode_ == accelerator.keycode_ + && modifiers_ == accelerator.modifiers_; +} + +bool Accelerator::KeyPressActivate() +{ + activated.emit(); + activated_ = true; + + return true; +} + +bool Accelerator::KeyReleaseActivate() +{ + activated.emit(); + activated_ = false; + + return true; +} + +bool Accelerator::HandleKeyPress(unsigned int keysym, + unsigned int modifiers, + PressedState pressed_state) +{ + auto is_modifier_only = keysym_ == 0 && keycode_ == 0 && modifiers_ != 0; + auto is_modifier_keysym = KeysymToModifier(keysym_); + auto modifier = KeysymToModifier(keysym); + + if (modifiers == 0) + { + /* We're pressing a key when no other key is pressed. We can reset this + * accelerator back to its original enabled state. */ + active_ = true; + activated_ = false; + } + + if (!active_) + return false; + + /* We need to cancel modifier-only accelerators in some cases. For example, + * we should cancel a Ctrl+Alt accelerator if Ctrl+Alt+T is pressed. */ + if (is_modifier_only || is_modifier_keysym) + { + if (!modifier) + { + /* We pressed a non-modifier key for a modifier-only accelerator. */ + active_ = false; + return false; + } + else if (keysym != keysym_ && (modifiers_ & modifier) == 0) + { + /* We pressed a modifier key that isn't the keysym and isn't one of the + * modifiers. */ + active_ = false; + return false; + } + } + else if (!modifier) + { + /* We expect a non-modifier key to activate and one was pressed. */ + if (modifiers == modifiers_) + { + /* The modifiers match. Check if the keysyms match. */ + if (keysym == keysym_) + return KeyPressActivate(); + else + { + /* Otherwise, check if the keycodes match. Maybe the accelerator + * specifies a particular key code, or the keyboard layout changed. For + * example, if the accelerator is Ctrl+A and the user switches from a + * QWERTY to an AZERTY layout, we should accept Ctrl+Q so the user can + * cycle through the entire list of keyboard layouts. */ + + GdkKeymapKey* key; + gint keys; + + if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), keysym, &key, &keys)) + { + for (auto i = 0; i < keys; i++) + { + if (key[i].keycode == keycode_) + { + g_free(key); + + return KeyPressActivate(); + } + } + + g_free(key); + } + } + } + } + + return false; +} + +bool Accelerator::HandleKeyRelease(unsigned int keysym, + unsigned int modifiers, + PressedState pressed_state) +{ + auto is_modifier_only = keysym_ == 0 && keycode_ == 0 && modifiers_ != 0; + auto is_modifier_keysym = KeysymToModifier(keysym_); + auto modifier = KeysymToModifier(keysym); + + /* Don't activate on key release if we were activated on a key press. */ + if (!active_ || activated_) + return false; + + /* Check if the keysyms match. */ + if (keysym == keysym_) + { + if (KeysymToModifier(keysym) == 0) + { + /* We released a non-modifier key. */ + if (modifiers == modifiers_) + return KeyReleaseActivate(); + } + else + { + /* We released a modifier key. */ + auto mirror_keysym = KeysymToMirrorKeysym(keysym); + auto is_mirror_pressed = (pressed_state & KeysymToPressedState(mirror_keysym)) != PressedState::NothingPressed; + + /* Ctrl+Shift_R is different from Ctrl+Shift+Shift_R, so we must detect + * if the mirror key was pressed or not. */ + if (is_mirror_pressed) + { + /* The mirrored modifier is pressed. */ + if (modifiers == modifiers_) + return KeyReleaseActivate(); + } + else + { + /* The mirrored modifier wasn't pressed. Compare modifiers without it. */ + if ((modifiers & ~KeysymToModifier(mirror_keysym)) == modifiers_) + return KeyReleaseActivate(); + } + } + } + + if (is_modifier_only || is_modifier_keysym) + { + if (modifier) + { + /* We released a modifier key for a modifier-only accelerator. */ + + if (is_modifier_only) + { + /* The accelerator has no keysym or keycode. */ + + /* TODO: Normally we would activate here, but compiz is intercepting + * this case and handling it. This is bad because now we can't do + * anything here. Otherwise we'll do the same action twice. */ + if (modifiers == modifiers_) + return false; + } + else + { + /* The accelerator has a modifier keysym. */ + auto is_keysym_pressed = (pressed_state & KeysymToPressedState(keysym_)) != PressedState::NothingPressed; + + if (is_keysym_pressed) + { + auto mirror_keysym = KeysymToMirrorKeysym(keysym_); + auto is_mirror_pressed = (pressed_state & KeysymToPressedState(mirror_keysym)) != PressedState::NothingPressed; + + /* Ctrl+Shift_R is different from Ctrl+Shift+Shift_R, so we must detect + * if the mirror key was pressed or not. */ + if (is_mirror_pressed) + { + /* The mirrored modifier is pressed. */ + if (modifiers == modifiers_) + return KeyReleaseActivate(); + } + else + { + /* The mirrored modifier wasn't pressed. Compare modifiers without it. */ + if ((modifiers & ~KeysymToModifier(mirror_keysym)) == modifiers_) + return KeyReleaseActivate(); + } + } + } + } + } + else if (keycode_ != 0 && modifiers == modifiers_) + { + /* Otherwise, check if the keycodes match. Maybe the accelerator + * specifies a particular key code, or the keyboard layout changed. For + * example, if the accelerator is Ctrl+A and the user switches from a + * QWERTY to an AZERTY layout, we should accept Ctrl+Q so the user can + * cycle through the entire list of keyboard layouts. */ + + GdkKeymapKey* key; + gint keys; + + if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), keysym, &key, &keys)) + { + for (auto i = 0; i < keys; i++) + { + if (key[i].keycode == keycode_) + { + g_free(key); + + return KeyReleaseActivate(); + } + } + + g_free(key); + } + } + + return false; +} + +Accelerators::Accelerators() + : pressed_state_(PressedState::NothingPressed) +{ +} + +void Accelerators::Clear() +{ + accelerators_.clear(); +} + +void Accelerators::Add(Accelerator::Ptr const& accelerator) +{ + accelerators_.push_back(accelerator); +} + +void Accelerators::Remove(Accelerator::Ptr const& accelerator) +{ + accelerators_.erase(std::remove(accelerators_.begin(), accelerators_.end(), accelerator), accelerators_.end()); +} + +bool Accelerators::HandleKeyPress(unsigned int keysym, + unsigned int modifiers) +{ + modifiers &= nux::KEY_MODIFIER_SHIFT + | nux::KEY_MODIFIER_CTRL + | nux::KEY_MODIFIER_ALT + | nux::KEY_MODIFIER_SUPER; + + switch (keysym) + { + case GDK_KEY_Shift_L: + pressed_state_ |= PressedState::LeftShiftPressed; + break; + case GDK_KEY_Shift_R: + pressed_state_ |= PressedState::RightShiftPressed; + break; + case GDK_KEY_Control_L: + pressed_state_ |= PressedState::LeftControlPressed; + break; + case GDK_KEY_Control_R: + pressed_state_ |= PressedState::RightControlPressed; + break; + case GDK_KEY_Meta_L: + case GDK_KEY_Alt_L: + pressed_state_ |= PressedState::LeftAltPressed; + break; + case GDK_KEY_Meta_R: + case GDK_KEY_Alt_R: + pressed_state_ |= PressedState::RightAltPressed; + break; + case GDK_KEY_Super_L: + pressed_state_ |= PressedState::LeftSuperPressed; + break; + case GDK_KEY_Super_R: + pressed_state_ |= PressedState::RightSuperPressed; + break; + } + + auto handled = false; + + for (auto& accelerator : accelerators_) + handled = accelerator->HandleKeyPress(keysym, modifiers, pressed_state_) || handled; + + return handled; +} + +bool Accelerators::HandleKeyRelease(unsigned int keysym, + unsigned int modifiers) +{ + modifiers &= nux::KEY_MODIFIER_SHIFT + | nux::KEY_MODIFIER_CTRL + | nux::KEY_MODIFIER_ALT + | nux::KEY_MODIFIER_SUPER; + + auto handled = false; + + for (auto& accelerator : accelerators_) + handled = accelerator->HandleKeyRelease(keysym, modifiers, pressed_state_) || handled; + + switch (keysym) + { + case GDK_KEY_Shift_L: + pressed_state_ &= ~PressedState::LeftShiftPressed; + break; + case GDK_KEY_Shift_R: + pressed_state_ &= ~PressedState::RightShiftPressed; + break; + case GDK_KEY_Control_L: + pressed_state_ &= ~PressedState::LeftControlPressed; + break; + case GDK_KEY_Control_R: + pressed_state_ &= ~PressedState::RightControlPressed; + break; + case GDK_KEY_Meta_L: + case GDK_KEY_Alt_L: + pressed_state_ &= ~PressedState::LeftAltPressed; + break; + case GDK_KEY_Meta_R: + case GDK_KEY_Alt_R: + pressed_state_ &= ~PressedState::RightAltPressed; + break; + case GDK_KEY_Super_L: + pressed_state_ &= ~PressedState::LeftSuperPressed; + break; + case GDK_KEY_Super_R: + pressed_state_ &= ~PressedState::RightSuperPressed; + break; + } + + return handled; +} + +} // lockscreen namespace +} // unity namespace diff --git a/lockscreen/LockScreenAccelerators.h b/lockscreen/LockScreenAccelerators.h new file mode 100644 index 000000000..7e8408252 --- /dev/null +++ b/lockscreen/LockScreenAccelerators.h @@ -0,0 +1,94 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* + * Copyright (C) 2014 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authored by: William Hua <william.hua@canonical.com> + */ + +#ifndef UNITY_LOCKSCREEN_ACCELERATORS +#define UNITY_LOCKSCREEN_ACCELERATORS + +#include <memory> +#include <sigc++/signal.h> +#include <vector> + + +namespace unity +{ +namespace lockscreen +{ + +enum class PressedState : unsigned int; + +class Accelerator +{ +public: + typedef std::shared_ptr<Accelerator> Ptr; + + Accelerator(unsigned int keysym, unsigned int keycode, unsigned int modifiers); + explicit Accelerator(std::string const& string); + + bool operator==(Accelerator const& accelerator) const; + + sigc::signal<void> activated; + +private: + bool KeyPressActivate(); + bool KeyReleaseActivate(); + + bool HandleKeyPress(unsigned int keysym, + unsigned int modifiers, + PressedState pressed_state); + bool HandleKeyRelease(unsigned int keysym, + unsigned int modifiers, + PressedState pressed_state); + + unsigned int keysym_; + unsigned int keycode_; + unsigned int modifiers_; + + bool active_; + bool activated_; + + friend class Accelerators; +}; + +class Accelerators +{ +public: + typedef std::shared_ptr<Accelerators> Ptr; + + Accelerators(); + + void Clear(); + + void Add(Accelerator::Ptr const& accelerator); + void Remove(Accelerator::Ptr const& accelerator); + + bool HandleKeyPress(unsigned int keysym, + unsigned int modifiers); + bool HandleKeyRelease(unsigned int keysym, + unsigned int modifiers); + +private: + std::vector<Accelerator::Ptr> accelerators_; + + PressedState pressed_state_; +}; + +} // lockscreen namespace +} // unity namespace + +#endif // UNITY_LOCKSCREEN_ACCELERATORS diff --git a/lockscreen/LockScreenController.cpp b/lockscreen/LockScreenController.cpp index 0314cf6fd..34fddaead 100644 --- a/lockscreen/LockScreenController.cpp +++ b/lockscreen/LockScreenController.cpp @@ -113,6 +113,7 @@ Controller::Controller(DBusManager::Ptr const& dbus_manager, shields_.clear(); upstart_wrapper_->Emit("desktop-unlock"); + accelerator_controller_.reset(); indicators_.reset(); } else if (!prompt_activation_) @@ -147,6 +148,12 @@ Controller::Controller(DBusManager::Ptr const& dbus_manager, }); } +void Controller::ActivatePanel() +{ + if (primary_shield_.IsValid()) + primary_shield_->ActivatePanel(); +} + void Controller::ResetPostLockScreenSaver() { screensaver_post_lock_timeout_.reset(); @@ -164,7 +171,6 @@ void Controller::OnPrimaryShieldMotion(int x, int y) primary_shield_->primary = false; primary_shield_ = shield; - primary_shield_->CheckCapsLockPrompt(); shield->primary = true; nux::GetWindowCompositor().SetAlwaysOnFrontWindow(primary_shield_.GetPointer()); auto move_cb = sigc::mem_fun(this, &Controller::OnPrimaryShieldMotion); @@ -193,7 +199,7 @@ void Controller::EnsureShields(std::vector<nux::Geometry> const& monitors) if (i >= shields_size) { - shield = shield_factory_->CreateShield(session_manager_, indicators_, i, i == primary); + shield = shield_factory_->CreateShield(session_manager_, indicators_, accelerator_controller_->GetAccelerators(), i, i == primary); is_new = true; } @@ -397,6 +403,12 @@ void Controller::LockScreen() indicators_ = std::make_shared<indicator::LockScreenDBusIndicators>(); upstart_wrapper_->Emit("desktop-lock"); + accelerator_controller_ = std::make_shared<AcceleratorController>(); + auto activate_key = WindowManager::Default().activate_indicators_key(); + auto accelerator = std::make_shared<Accelerator>(activate_key.second, 0, activate_key.first); + accelerator->activated.connect(std::bind(std::mem_fn(&Controller::ActivatePanel), this)); + accelerator_controller_->GetAccelerators()->Add(accelerator); + ShowShields(); } diff --git a/lockscreen/LockScreenController.h b/lockscreen/LockScreenController.h index 1c43fa49a..8e53269b7 100644 --- a/lockscreen/LockScreenController.h +++ b/lockscreen/LockScreenController.h @@ -25,6 +25,7 @@ #include <UnityCore/GLibSource.h> #include "LockScreenShieldFactory.h" +#include "LockScreenAcceleratorController.h" #include "ScreenSaverDBusManager.h" #include "unity-shared/BackgroundEffectHelper.h" #include "unity-shared/UpstartWrapper.h" @@ -60,6 +61,7 @@ private: void BlankWindowGrabEnable(bool grab); void SimulateActivity(); void ResetPostLockScreenSaver(); + void ActivatePanel(); void OnLockRequested(bool prompt); void OnUnlockRequested(); @@ -74,6 +76,7 @@ private: DBusManager::Ptr dbus_manager_; session::Manager::Ptr session_manager_; indicator::Indicators::Ptr indicators_; + AcceleratorController::Ptr accelerator_controller_; UpstartWrapper::Ptr upstart_wrapper_; ShieldFactoryInterface::Ptr shield_factory_; @@ -99,4 +102,4 @@ private: } } -#endif \ No newline at end of file +#endif diff --git a/lockscreen/LockScreenPanel.cpp b/lockscreen/LockScreenPanel.cpp index c3b5a934d..71dc3397e 100644 --- a/lockscreen/LockScreenPanel.cpp +++ b/lockscreen/LockScreenPanel.cpp @@ -21,7 +21,6 @@ #include <boost/algorithm/string/trim.hpp> #include <Nux/HLayout.h> -#include <UnityCore/Variant.h> #include "LockScreenSettings.h" #include "panel/PanelIndicatorsView.h" @@ -39,30 +38,6 @@ namespace lockscreen namespace { const RawPixel PADDING = 5_em; - -const std::string MEDIA_KEYS_SCHEMA = "org.gnome.settings-daemon.plugins.media-keys"; -const std::string MEDIA_KEYS_VOLUME_MUTE = "volume-mute"; -const std::string MEDIA_KEYS_VOLUME_DOWN = "volume-down"; -const std::string MEDIA_KEYS_VOLUME_UP = "volume-up"; -const std::string INPUT_SWITCH_SCHEMA = "org.gnome.desktop.wm.keybindings"; -const std::string INPUT_SWITCH_PREVIOUS = "switch-input-source-backward"; -const std::string INPUT_SWITCH_NEXT = "switch-input-source"; - -const std::string INDICATOR_KEYBOARD_BUS_NAME = "com.canonical.indicator.keyboard"; -const std::string INDICATOR_KEYBOARD_OBJECT_PATH = "/com/canonical/indicator/keyboard"; -const std::string INDICATOR_SOUND_BUS_NAME = "com.canonical.indicator.sound"; -const std::string INDICATOR_SOUND_OBJECT_PATH = "/com/canonical/indicator/sound"; -const std::string INDICATOR_ACTION_INTERFACE = "org.gtk.Actions"; - -const std::string INDICATOR_KEYBOARD_ACTION_SCROLL = "locked_scroll"; -const std::string INDICATOR_SOUND_ACTION_SCROLL = "scroll"; -const std::string INDICATOR_SOUND_ACTION_MUTE = "mute"; - -const unsigned int MODIFIERS = nux::KEY_MODIFIER_SHIFT | - nux::KEY_MODIFIER_CAPS_LOCK | - nux::KEY_MODIFIER_CTRL | - nux::KEY_MODIFIER_ALT | - nux::KEY_MODIFIER_SUPER; } using namespace indicator; @@ -74,8 +49,6 @@ Panel::Panel(int monitor_, Indicators::Ptr const& indicators, session::Manager:: , monitor(monitor_) , indicators_(indicators) , needs_geo_sync_(true) - , media_key_settings_(g_settings_new(MEDIA_KEYS_SCHEMA.c_str())) - , input_switch_settings_(g_settings_new(INPUT_SWITCH_SCHEMA.c_str())) { double scale = unity::Settings::Instance().em(monitor)->DPIScale(); auto* layout = new nux::HLayout(); @@ -114,11 +87,6 @@ Panel::Panel(int monitor_, Indicators::Ptr const& indicators, session::Manager:: BuildTexture(); QueueRelayout(); }); - - ParseAccelerators(); - - key_down.connect(sigc::mem_fun(this, &Panel::OnKeyDown)); - key_up.connect(sigc::mem_fun(this, &Panel::OnKeyUp)); } void Panel::BuildTexture() @@ -180,12 +148,6 @@ void Panel::OnEntryActivateRequest(std::string const& entry_id) indicators_view_->ActivateEntry(entry_id, 0); } -void Panel::ActivateFirst() -{ - if (GetInputEventSensitivity()) - indicators_view_->ActivateIfSensitive(); -} - void Panel::OnEntryActivated(std::string const& panel, std::string const& entry_id, nux::Rect const&) { if (!GetInputEventSensitivity() || (!panel.empty() && panel != GetPanelName())) @@ -254,210 +216,15 @@ void Panel::Draw(nux::GraphicsEngine& graphics_engine, bool force_draw) } } -Panel::Accelerator Panel::ParseAcceleratorString(std::string const& string) const -{ - guint gtk_key; - GdkModifierType gtk_modifiers; - gtk_accelerator_parse(string.c_str(), >k_key, >k_modifiers); - - unsigned int nux_key = gtk_key; - unsigned int nux_modifiers = 0; - - if (gtk_modifiers & GDK_SHIFT_MASK) - nux_modifiers |= nux::KEY_MODIFIER_SHIFT; - if (gtk_modifiers & GDK_LOCK_MASK) - nux_modifiers |= nux::KEY_MODIFIER_CAPS_LOCK; - if (gtk_modifiers & GDK_CONTROL_MASK) - nux_modifiers |= nux::KEY_MODIFIER_CTRL; - if (gtk_modifiers & GDK_MOD1_MASK) - nux_modifiers |= nux::KEY_MODIFIER_ALT; - if (gtk_modifiers & GDK_SUPER_MASK) - nux_modifiers |= nux::KEY_MODIFIER_SUPER; - - return std::make_pair(nux_modifiers, nux_key); -} - -void Panel::ParseAccelerators() -{ - activate_indicator_ = WindowManager::Default().activate_indicators_key(); - volume_mute_ = ParseAcceleratorString(glib::String(g_settings_get_string(media_key_settings_, MEDIA_KEYS_VOLUME_MUTE.c_str()))); - volume_down_ = ParseAcceleratorString(glib::String(g_settings_get_string(media_key_settings_, MEDIA_KEYS_VOLUME_DOWN.c_str()))); - volume_up_ = ParseAcceleratorString(glib::String(g_settings_get_string(media_key_settings_, MEDIA_KEYS_VOLUME_UP.c_str()))); - - auto variant = glib::Variant(g_settings_get_value(input_switch_settings_, INPUT_SWITCH_PREVIOUS.c_str()), glib::StealRef()); - - if (g_variant_n_children(variant) > 0) - { - const gchar *accelerator; - g_variant_get_child(variant, 0, "&s", &accelerator); - previous_source_ = ParseAcceleratorString(accelerator); - } - else - previous_source_ = std::make_pair(0, 0); - - variant = glib::Variant(g_settings_get_value(input_switch_settings_, INPUT_SWITCH_NEXT.c_str()), glib::StealRef()); - - if (g_variant_n_children(variant) > 0) - { - const gchar *accelerator; - g_variant_get_child(variant, 0, "&s", &accelerator); - next_source_ = ParseAcceleratorString(accelerator); - } - else - next_source_ = std::make_pair(0, 0); -} - -bool Panel::WillHandleKeyEvent(unsigned int event_type, unsigned long key_sym, unsigned long modifiers) -{ - auto is_press = event_type == nux::EVENT_KEY_DOWN; - - /* If we're just pressing a key, and no modifiers are pressed, then - * we can start accepting new actions again. */ - if (is_press && (modifiers & MODIFIERS) == 0) - last_action_ = std::make_pair(0, 0); - - return IsMatch(is_press, key_sym, modifiers, activate_indicator_) || - IsMatch(is_press, key_sym, modifiers, volume_mute_) || - IsMatch(is_press, key_sym, modifiers, volume_down_) || - IsMatch(is_press, key_sym, modifiers, volume_up_) || - IsMatch(is_press, key_sym, modifiers, previous_source_) || - IsMatch(is_press, key_sym, modifiers, next_source_); -} - bool Panel::InspectKeyEvent(unsigned int event_type, unsigned int keysym, const char*) { return true; } -bool Panel::IsMatch(bool is_press, - unsigned int key_sym, - unsigned int state, - Accelerator const& accelerator) const +void Panel::ActivatePanel() { - /* Do the easy check, just compare key codes and modifiers. - * TODO: Check permutations of modifier-only shortcuts. */ - return key_sym == accelerator.second && (state & MODIFIERS) == accelerator.first; -} - -void Panel::OnKeyDown(unsigned long event, - unsigned long key_sym, - unsigned long state, - const char* text, - unsigned short repeat) -{ - if (IsMatch(true, key_sym, state, activate_indicator_)) - { - ActivateFirst(); - last_action_ = activate_indicator_; - } - else if (IsMatch(true, key_sym, state, volume_mute_)) - { - ActivateSoundAction(INDICATOR_SOUND_ACTION_MUTE); - last_action_ = volume_mute_; - } - else if (IsMatch(true, key_sym, state, volume_down_)) - { - ActivateSoundAction(INDICATOR_SOUND_ACTION_SCROLL, g_variant_new_int32(-1)); - last_action_ = volume_down_; - } - else if (IsMatch(true, key_sym, state, volume_up_)) - { - ActivateSoundAction(INDICATOR_SOUND_ACTION_SCROLL, g_variant_new_int32(+1)); - last_action_ = volume_up_; - } - else if (IsMatch(true, key_sym, state, previous_source_)) - { - ActivateKeyboardAction(INDICATOR_KEYBOARD_ACTION_SCROLL, g_variant_new_int32(-1)); - last_action_ = previous_source_; - } - else if (IsMatch(true, key_sym, state, next_source_)) - { - ActivateKeyboardAction(INDICATOR_KEYBOARD_ACTION_SCROLL, g_variant_new_int32(+1)); - last_action_ = next_source_; - } -} - -void Panel::OnKeyUp(unsigned int key_sym, - unsigned long key_code, - unsigned long state) -{ - /* We only want to act if we didn't activate the action on key - * down. Once we see the key up, we can start accepting actions - * again. */ - - if (IsMatch(false, key_sym, state, activate_indicator_)) - { - if (last_action_ != activate_indicator_) - ActivateFirst(); - - last_action_ = std::make_pair(0, 0); - } - else if (IsMatch(false, key_sym, state, volume_mute_)) - { - if (last_action_ != volume_mute_) - ActivateSoundAction(INDICATOR_SOUND_ACTION_MUTE); - - last_action_ = std::make_pair(0, 0); - } - else if (IsMatch(false, key_sym, state, volume_down_)) - { - if (last_action_ != volume_down_) - ActivateSoundAction(INDICATOR_SOUND_ACTION_SCROLL, g_variant_new_int32(-1)); - - last_action_ = std::make_pair(0, 0); - } - else if (IsMatch(false, key_sym, state, volume_up_)) - { - if (last_action_ != volume_up_) - ActivateSoundAction(INDICATOR_SOUND_ACTION_SCROLL, g_variant_new_int32(+1)); - - last_action_ = std::make_pair(0, 0); - } - else if (IsMatch(false, key_sym, state, previous_source_)) - { - if (last_action_ != previous_source_) - ActivateKeyboardAction(INDICATOR_KEYBOARD_ACTION_SCROLL, g_variant_new_int32(-1)); - - last_action_ = std::make_pair(0, 0); - } - else if (IsMatch(false, key_sym, state, next_source_)) - { - if (last_action_ != next_source_) - ActivateKeyboardAction(INDICATOR_KEYBOARD_ACTION_SCROLL, g_variant_new_int32(+1)); - - last_action_ = std::make_pair(0, 0); - } -} - -void Panel::ActivateIndicatorAction(std::string const& bus_name, - std::string const& object_path, - std::string const& action, - glib::Variant const& parameter) const -{ - GVariantBuilder builder; - - g_variant_builder_init(&builder, G_VARIANT_TYPE("(sava{sv})")); - g_variant_builder_add(&builder, "s", action.c_str()); - - if (parameter) - g_variant_builder_add_parsed(&builder, "[%v]", (GVariant*) parameter); - else - g_variant_builder_add_parsed(&builder, "@av []"); - - g_variant_builder_add_parsed(&builder, "@a{sv} []"); - - auto proxy = std::make_shared<glib::DBusProxy>(bus_name, object_path, INDICATOR_ACTION_INTERFACE, G_BUS_TYPE_SESSION); - proxy->CallBegin("Activate", g_variant_builder_end(&builder), [proxy] (GVariant*, glib::Error const&) {}); -} - -void Panel::ActivateKeyboardAction(std::string const& action, glib::Variant const& parameter) const -{ - ActivateIndicatorAction(INDICATOR_KEYBOARD_BUS_NAME, INDICATOR_KEYBOARD_OBJECT_PATH, action, parameter); -} - -void Panel::ActivateSoundAction(std::string const& action, glib::Variant const& parameter) const -{ - ActivateIndicatorAction(INDICATOR_SOUND_BUS_NAME, INDICATOR_SOUND_OBJECT_PATH, action, parameter); + if (GetInputEventSensitivity()) + indicators_view_->ActivateIfSensitive(); } } diff --git a/lockscreen/LockScreenPanel.h b/lockscreen/LockScreenPanel.h index 8c31108a5..c5397b617 100644 --- a/lockscreen/LockScreenPanel.h +++ b/lockscreen/LockScreenPanel.h @@ -22,10 +22,8 @@ #include <Nux/Nux.h> #include <Nux/View.h> -#include <UnityCore/GLibDBusProxy.h> -#include <UnityCore/GLibSource.h> -#include <UnityCore/GLibWrapper.h> #include <UnityCore/Indicators.h> +#include <UnityCore/GLibSource.h> #include <UnityCore/SessionManager.h> namespace unity @@ -46,7 +44,7 @@ public: nux::Property<bool> active; nux::Property<int> monitor; - bool WillHandleKeyEvent(unsigned int event_type, unsigned long key_sym, unsigned long modifiers); + void ActivatePanel(); protected: void Draw(nux::GraphicsEngine& GfxContext, bool force_draw) override; @@ -71,52 +69,6 @@ private: bool needs_geo_sync_; nux::Point tracked_pointer_pos_; glib::Source::UniquePtr track_menu_pointer_timeout_; - - glib::Object<GSettings> media_key_settings_; - glib::Object<GSettings> input_switch_settings_; - - typedef std::pair<unsigned int, unsigned int> Accelerator; - Accelerator ParseAcceleratorString(std::string const& string) const; - - void ParseAccelerators(); - - Accelerator activate_indicator_; - Accelerator volume_mute_; - Accelerator volume_down_; - Accelerator volume_up_; - Accelerator previous_source_; - Accelerator next_source_; - - /* We only want to activate the indicator on key press OR key - * release, never both, so we'll need to keep track of the last - * action that occurred. However, holding the keys down should - * allow multiple activations, for example, when the volume - * down button is held down. */ - Accelerator last_action_; - - bool IsMatch(bool is_press, - unsigned int key_sym, - unsigned int modifiers, - Accelerator const& accelerator) const; - - void OnKeyDown(unsigned long event, - unsigned long key_sym, - unsigned long state, - const char* text, - unsigned short repeat); - - void OnKeyUp(unsigned int key_sym, - unsigned long key_code, - unsigned long state); - - /* This is just for telling an indicator to do something. */ - void ActivateIndicatorAction(std::string const& bus_name, - std::string const& object_path, - std::string const& action, - glib::Variant const& parameter = glib::Variant()) const; - - void ActivateKeyboardAction(std::string const& action, glib::Variant const& parameter = glib::Variant()) const; - void ActivateSoundAction(std::string const& action, glib::Variant const& parameter = glib::Variant()) const; }; } // lockscreen namespace diff --git a/lockscreen/LockScreenShield.cpp b/lockscreen/LockScreenShield.cpp index 861beb176..50dd1e42e 100644 --- a/lockscreen/LockScreenShield.cpp +++ b/lockscreen/LockScreenShield.cpp @@ -36,8 +36,8 @@ namespace unity namespace lockscreen { -Shield::Shield(session::Manager::Ptr const& session_manager, indicator::Indicators::Ptr const& indicators, int monitor_num, bool is_primary) - : AbstractShield(session_manager, indicators, monitor_num, is_primary) +Shield::Shield(session::Manager::Ptr const& session_manager, indicator::Indicators::Ptr const& indicators, Accelerators::Ptr const& accelerators, int monitor_num, bool is_primary) + : AbstractShield(session_manager, indicators, accelerators, monitor_num, is_primary) , bg_settings_(std::make_shared<BackgroundSettings>()) , prompt_view_(nullptr) , panel_view_(nullptr) @@ -86,12 +86,6 @@ void Shield::UpdateBackgroundTexture() } } -void Shield::CheckCapsLockPrompt() -{ - if (prompt_view_) - prompt_view_->CheckIfCapsLockOn(); -} - void Shield::ShowPrimaryView() { GrabPointer(); @@ -189,14 +183,25 @@ UserPromptView* Shield::CreatePromptView() return prompt_view; } -nux::Area* Shield::FindKeyFocusArea(unsigned etype, unsigned long key_sym, unsigned long modifiers) +nux::Area* Shield::FindKeyFocusArea(unsigned etype, unsigned long keysym, unsigned long modifiers) { if (primary) { - grab_key.emit(modifiers, key_sym); + grab_key.emit(modifiers, keysym); - if (panel_view_ && panel_view_->WillHandleKeyEvent(etype, key_sym, modifiers)) - return panel_view_; + if (accelerators_) + { + if (etype == nux::EVENT_KEY_DOWN) + { + if (accelerators_->HandleKeyPress(keysym, modifiers)) + return panel_view_; + } + else if (etype == nux::EVENT_KEY_UP) + { + if (accelerators_->HandleKeyRelease(keysym, modifiers)) + return panel_view_; + } + } if (prompt_view_) { @@ -230,5 +235,11 @@ bool Shield::IsIndicatorOpen() const return panel_view_ ? panel_view_->active() : false; } +void Shield::ActivatePanel() +{ + if (panel_view_) + panel_view_->ActivatePanel(); +} + } } diff --git a/lockscreen/LockScreenShield.h b/lockscreen/LockScreenShield.h index c95633385..25846334d 100644 --- a/lockscreen/LockScreenShield.h +++ b/lockscreen/LockScreenShield.h @@ -36,10 +36,10 @@ class Panel; class Shield : public AbstractShield { public: - Shield(session::Manager::Ptr const&, indicator::Indicators::Ptr const&, int monitor, bool is_primary); + Shield(session::Manager::Ptr const&, indicator::Indicators::Ptr const&, Accelerators::Ptr const&, int monitor, bool is_primary); bool IsIndicatorOpen() const override; - void CheckCapsLockPrompt() override; + void ActivatePanel() override; protected: bool AcceptKeyNavFocus() override; diff --git a/lockscreen/LockScreenShieldFactory.cpp b/lockscreen/LockScreenShieldFactory.cpp index 4932ca5c4..6bed3db27 100644 --- a/lockscreen/LockScreenShieldFactory.cpp +++ b/lockscreen/LockScreenShieldFactory.cpp @@ -25,9 +25,9 @@ namespace unity namespace lockscreen { -nux::ObjectPtr<AbstractShield> ShieldFactory::CreateShield(session::Manager::Ptr const& session_manager, indicator::Indicators::Ptr const& indicators, int monitor, bool is_primary) +nux::ObjectPtr<AbstractShield> ShieldFactory::CreateShield(session::Manager::Ptr const& session_manager, indicator::Indicators::Ptr const& indicators, Accelerators::Ptr const& accelerators, int monitor, bool is_primary) { - return nux::ObjectPtr<Shield>(new Shield(session_manager, indicators, monitor, is_primary)); + return nux::ObjectPtr<Shield>(new Shield(session_manager, indicators, accelerators, monitor, is_primary)); } } diff --git a/lockscreen/LockScreenShieldFactory.h b/lockscreen/LockScreenShieldFactory.h index 19b3894fa..581feba9f 100644 --- a/lockscreen/LockScreenShieldFactory.h +++ b/lockscreen/LockScreenShieldFactory.h @@ -37,12 +37,12 @@ struct ShieldFactoryInterface virtual ~ShieldFactoryInterface() = default; - virtual nux::ObjectPtr<AbstractShield> CreateShield(session::Manager::Ptr const&, indicator::Indicators::Ptr const&, int monitor, bool is_primary) = 0; + virtual nux::ObjectPtr<AbstractShield> CreateShield(session::Manager::Ptr const&, indicator::Indicators::Ptr const&, Accelerators::Ptr const&, int monitor, bool is_primary) = 0; }; struct ShieldFactory : ShieldFactoryInterface { - nux::ObjectPtr<AbstractShield> CreateShield(session::Manager::Ptr const&, indicator::Indicators::Ptr const&, int monitor, bool is_primary) override; + nux::ObjectPtr<AbstractShield> CreateShield(session::Manager::Ptr const&, indicator::Indicators::Ptr const&, Accelerators::Ptr const&, int monitor, bool is_primary) override; }; } diff --git a/lockscreen/UserPromptView.cpp b/lockscreen/UserPromptView.cpp index 2e852ceff..5c99fefe9 100644 --- a/lockscreen/UserPromptView.cpp +++ b/lockscreen/UserPromptView.cpp @@ -21,12 +21,10 @@ #include <boost/algorithm/string/trim.hpp> #include <Nux/VLayout.h> -#include <X11/XKBlib.h> #include "LockScreenSettings.h" #include "unity-shared/CairoTexture.h" #include "unity-shared/DashStyle.h" -#include "unity-shared/PreviewStyle.h" #include "unity-shared/TextInput.h" #include "unity-shared/StaticCairoText.h" #include "unity-shared/RawPixel.h" @@ -40,9 +38,9 @@ namespace const RawPixel PADDING = 10_em; const RawPixel LAYOUT_MARGIN = 10_em; const RawPixel MSG_LAYOUT_MARGIN = 15_em; -const RawPixel PROMPT_LAYOUT_MARGIN = 5_em; +const RawPixel PROMPT_LAYOUT_MARGIN = 5_em; -const int PROMPT_FONT_SIZE = 13; +const int PROMPT_FONT_SIZE = 13; nux::AbstractPaintLayer* CrateBackgroundLayer(int width, int height) { @@ -78,28 +76,6 @@ nux::AbstractPaintLayer* CrateBackgroundLayer(int width, int height) rop)); } -nux::AbstractPaintLayer* CreateWarningLayer(nux::BaseTexture* texture) -{ - // Create the texture layer - nux::TexCoordXForm texxform; - - texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); - texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_REPEAT); - texxform.min_filter = nux::TEXFILTER_LINEAR; - texxform.mag_filter = nux::TEXFILTER_LINEAR; - - nux::ROPConfig rop; - rop.Blend = true; - rop.SrcBlend = GL_ONE; - rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA; - - return (new nux::TextureLayer(texture->GetDeviceTexture(), - texxform, - nux::color::White, - true, - rop)); -} - std::string SanitizeMessage(std::string const& message) { std::string msg = boost::algorithm::trim_copy(message); @@ -124,7 +100,6 @@ std::string SanitizeMessage(std::string const& message) UserPromptView::UserPromptView(session::Manager::Ptr const& session_manager) : nux::View(NUX_TRACKER_LOCATION) , session_manager_(session_manager) - , caps_lock_on_(false) { user_authenticator_.echo_on_requested.connect([this](std::string const& message, PromiseAuthCodePtr const& promise){ AddPrompt(message, /* visible */ true, promise); @@ -146,32 +121,10 @@ UserPromptView::UserPromptView(session::Manager::Ptr const& session_manager) ResetLayout(); }); - dash::previews::Style& preview_style = dash::previews::Style::Instance(); - - warning_ = preview_style.GetWarningIcon(); ResetLayout(); user_authenticator_.AuthenticateStart(session_manager_->UserName(), sigc::mem_fun(this, &UserPromptView::AuthenticationCb)); - - // When we get to HiDPI changes here, we will need to update this width - dash::Style& style = dash::Style::Instance(); - spin_icon_width_ = style.GetSearchSpinIcon()->GetWidth(); - - CheckIfCapsLockOn(); -} - -void UserPromptView::CheckIfCapsLockOn() -{ - Display *dpy = nux::GetGraphicsDisplay()->GetX11Display(); - unsigned int state = 0; - XkbGetIndicatorState(dpy, XkbUseCoreKbd, &state); - - // Caps is on 0x1, couldn't find any #define in /usr/include/X11 - if ((state & 0x1) == 1) - caps_lock_on_ = true; - else - caps_lock_on_ = false; } bool UserPromptView::InspectKeyEvent(unsigned int eventType, unsigned int key_sym, const char* character) @@ -179,7 +132,7 @@ bool UserPromptView::InspectKeyEvent(unsigned int eventType, unsigned int key_sy if ((eventType == nux::NUX_KEYDOWN) && (key_sym == NUX_VK_ESCAPE)) { if (!focus_queue_.empty()) - focus_queue_.front()->SetText(""); + focus_queue_.front()->text_entry()->SetText(""); return true; } @@ -260,15 +213,6 @@ void UserPromptView::DrawContent(nux::GraphicsEngine& graphics_engine, bool forc nux::GetPainter().PushLayer(graphics_engine, geo, bg_layer_.get()); } - if (caps_lock_on_) - { - for (auto const& text_entry : focus_queue_) - PaintWarningIcon(graphics_engine, text_entry->GetGeometry()); - - if (focus_queue_.empty()) - PaintWarningIcon(graphics_engine, cached_focused_geo_); - } - if (GetLayout()) GetLayout()->ProcessDraw(graphics_engine, force_draw); @@ -278,49 +222,16 @@ void UserPromptView::DrawContent(nux::GraphicsEngine& graphics_engine, bool forc graphics_engine.PopClippingRectangle(); } -void UserPromptView::PaintWarningIcon(nux::GraphicsEngine& graphics_engine, nux::Geometry const& geo) -{ - nux::Geometry warning_geo = {geo.x + geo.width - GetWarningIconOffset(), - geo.y, warning_->GetWidth(), warning_->GetHeight()}; - - nux::GetPainter().PushLayer(graphics_engine, warning_geo, CreateWarningLayer(warning_)); -} - -int UserPromptView::GetWarningIconOffset() -{ - return warning_->GetWidth() + spin_icon_width_; -} - nux::View* UserPromptView::focus_view() { if (focus_queue_.empty()) return nullptr; for (auto* view : focus_queue_) - if (view->HasKeyboardFocus()) + if (view->text_entry()->HasKeyboardFocus()) return view; - return focus_queue_.front(); -} - -void UserPromptView::ToggleCapsLockBool() -{ - caps_lock_on_ = !caps_lock_on_; - QueueDraw(); -} - -void UserPromptView::RecvKeyUp(unsigned keysym, - unsigned long keycode, - unsigned long state) -{ - if (!caps_lock_on_ && keysym == NUX_VK_CAPITAL) - { - ToggleCapsLockBool(); - } - else if (caps_lock_on_ && keysym == NUX_VK_CAPITAL) - { - ToggleCapsLockBool(); - } + return focus_queue_.front()->text_entry(); } void UserPromptView::AddPrompt(std::string const& message, bool visible, PromiseAuthCodePtr const& promise) @@ -330,18 +241,15 @@ void UserPromptView::AddPrompt(std::string const& message, bool visible, Promise text_input->input_hint = SanitizeMessage(message); text_input->hint_font_size = PROMPT_FONT_SIZE; + text_input->show_caps_lock = true; text_entry->SetPasswordMode(!visible); text_entry->SetPasswordChar("•"); text_entry->SetToggleCursorVisibilityOnKeyFocus(true); - text_entry->key_up.connect(sigc::mem_fun(this, &UserPromptView::RecvKeyUp)); - text_input->SetMinimumHeight(Settings::GRID_SIZE); text_input->SetMaximumHeight(Settings::GRID_SIZE); prompt_layout_->AddView(text_input, 1); - focus_queue_.push_back(text_entry); - - CheckIfCapsLockOn(); + focus_queue_.push_back(text_input); // Don't remove it, it helps with a11y. if (focus_queue_.size() == 1) @@ -378,10 +286,12 @@ void UserPromptView::AddPrompt(std::string const& message, bool visible, Promise void UserPromptView::AddMessage(std::string const& message, nux::Color const& color) { + nux::Geometry const& geo = GetGeometry(); auto* view = new unity::StaticCairoText(""); view->SetFont(Settings::Instance().font_name()); view->SetTextColor(color); view->SetText(message); + view->SetMaximumWidth(geo.width); msg_layout_->AddView(view); diff --git a/lockscreen/UserPromptView.h b/lockscreen/UserPromptView.h index 7e98dc0af..4b3061565 100644 --- a/lockscreen/UserPromptView.h +++ b/lockscreen/UserPromptView.h @@ -56,8 +56,6 @@ public: void AddMessage(std::string const& message, nux::Color const& color); void AuthenticationCb(bool authenticated); - void CheckIfCapsLockOn(); - protected: void Draw(nux::GraphicsEngine& graphics_engine, bool force_draw) override; void DrawContent(nux::GraphicsEngine& graphics_engine, bool force_draw) override; @@ -66,12 +64,6 @@ private: void ResetLayout(); bool InspectKeyEvent(unsigned int eventType, unsigned int key_sym, const char* character); - void RecvKeyUp(unsigned int, unsigned long, unsigned long); - - void PaintWarningIcon(nux::GraphicsEngine& graphics_engine, nux::Geometry const& geo); - void ToggleCapsLockBool(); - - int GetWarningIconOffset(); session::Manager::Ptr session_manager_; UserAuthenticatorPam user_authenticator_; @@ -81,13 +73,9 @@ private: StaticCairoText* message_; StaticCairoText* error_; StaticCairoText* invalid_login_; - std::deque<IMTextEntry*> focus_queue_; + std::deque<TextInput*> focus_queue_; - nux::BaseTexture* warning_; nux::Geometry cached_focused_geo_; - - bool caps_lock_on_; - int spin_icon_width_; }; } diff --git a/plugins/unityshell/src/unityshell.cpp b/plugins/unityshell/src/unityshell.cpp index dfc4a70ac..170e2bd1b 100644 --- a/plugins/unityshell/src/unityshell.cpp +++ b/plugins/unityshell/src/unityshell.cpp @@ -101,7 +101,7 @@ using util::Timer; DECLARE_LOGGER(logger, "unity.shell.compiz"); namespace { -UnityScreen* uScreen = 0; +UnityScreen* uScreen = nullptr; void reset_glib_logging(); void configure_logging(); @@ -143,6 +143,7 @@ const int FRAMES_TO_REDRAW_ON_RESUME = 10; const RawPixel SCALE_PADDING = 40_em; const RawPixel SCALE_SPACING = 20_em; const std::string RELAYOUT_TIMEOUT = "relayout-timeout"; +const std::string HUD_UNGRAB_WAIT = "hud-ungrab-wait"; const std::string FIRST_RUN_STAMP = "first_run.stamp"; const std::string LOCKED_STAMP = "locked.stamp"; } // namespace local @@ -167,6 +168,8 @@ UnityScreen::UnityScreen(CompScreen* screen) , _key_nav_mode_requested(false) , _last_output(nullptr) , force_draw_countdown_(0) + , firstWindowAboveShell(nullptr) + , onboard_(nullptr) , grab_index_(0) , painting_tray_ (false) , last_scroll_event_(0) @@ -521,6 +524,7 @@ void UnityScreen::initAltTabNextWindow() void UnityScreen::OnInitiateSpread() { + scale_just_activated_ = super_keypressed_; spread_filter_ = std::make_shared<spread::Filter>(); spread_filter_->text.changed.connect([this] (std::string const& filter) { if (filter.empty()) @@ -939,7 +943,8 @@ void UnityScreen::DrawPanelUnderDash() bool UnityScreen::forcePaintOnTop() { - return !allowWindowPaint || + return !allowWindowPaint || + lockscreen_controller_->IsLocked() || ((switcher_controller_->Visible() || WindowManager::Default().IsExpoActive()) && !fullscreen_windows_.empty () && (!(screen->grabbed () && !screen->otherGrabExist (NULL)))); @@ -1184,6 +1189,20 @@ bool UnityWindow::IsMinimized () return window->minimized (); } +bool UnityWindow::CanBypassLockScreen() const +{ + if (window->type() == CompWindowTypePopupMenuMask && + uScreen->lockscreen_controller_->HasOpenMenu()) + { + return true; + } + + if (window == uScreen->onboard_) + return true; + + return false; +} + void UnityWindow::DoOverrideFrameRegion(CompRegion ®ion) { unsigned int oldUpdateFrameRegionIndex = window->updateFrameRegionGetCurrentIndex(); @@ -1703,6 +1722,8 @@ void UnityScreen::handleEvent(XEvent* event) wm.OnScreenGrabbed(); else if (event->xfocus.mode == NotifyUngrab) wm.OnScreenUngrabbed(); + else if (!screen->grabbed() && event->xfocus.mode == NotifyWhileGrabbed) + wm.OnScreenGrabbed(); if (_key_nav_mode_requested) { @@ -1981,19 +2002,7 @@ void UnityScreen::handleCompizEvent(const char* plugin, { PluginAdapter& adapter = PluginAdapter::Default(); adapter.NotifyCompizEvent(plugin, event, option); - compiz::CompizMinimizedWindowHandler<UnityScreen, UnityWindow>::handleCompizEvent (plugin, event, option); - - if (launcher_controller_->IsOverlayOpen() && g_strcmp0(event, "start_viewport_switch") == 0) - { - ubus_manager_.SendMessage(UBUS_OVERLAY_CLOSE_REQUEST); - } - - if (super_keypressed_ && g_strcmp0(plugin, "scale") == 0 && - g_strcmp0(event, "activate") == 0) - { - scale_just_activated_ = CompOption::getBoolOptionNamed(option, "active"); - } - + compiz::CompizMinimizedWindowHandler<UnityScreen, UnityWindow>::handleCompizEvent(plugin, event, option); screen->handleCompizEvent(plugin, event, option); } @@ -2062,6 +2071,7 @@ bool UnityScreen::showLauncherKeyTerminate(CompAction* action, return false; bool was_tap = state & CompAction::StateTermTapped; + bool tap_handled = false; LOG_DEBUG(logger) << "Super released: " << (was_tap ? "tapped" : "released"); int when = options[7].value().i(); // XEvent time in millisec @@ -2090,6 +2100,24 @@ bool UnityScreen::showLauncherKeyTerminate(CompAction* action, { QuicklistManager::Default()->Current()->Hide(); } + + if (!dash_controller_->IsVisible()) + { + if (!adapter.IsTopWindowFullscreenOnMonitorWithMouse()) + { + if (dash_controller_->ShowDash()) + { + tap_handled = true; + ubus_manager_.SendMessage(UBUS_PLACE_ENTRY_ACTIVATE_REQUEST, + g_variant_new("(sus)", "home.scope", dash::GOTO_DASH_URI, "")); + } + } + } + else + { + dash_controller_->HideDash(); + tap_handled = true; + } } super_keypressed_ = false; @@ -2103,7 +2131,7 @@ bool UnityScreen::showLauncherKeyTerminate(CompAction* action, EnableCancelAction(CancelActionTarget::SHORTCUT_HINT, false); action->setState (action->state() & (unsigned)~(CompAction::StateTermKey)); - return true; + return (was_tap && tap_handled) || !was_tap; } bool UnityScreen::showPanelFirstMenuKeyInitiate(CompAction* action, @@ -2471,32 +2499,45 @@ bool UnityScreen::ShowHud() { if (switcher_controller_->Visible()) { - LOG_ERROR(logger) << "this should never happen"; + LOG_ERROR(logger) << "Switcher is visible when showing HUD: this should never happen"; return false; // early exit if the switcher is open } - if (PluginAdapter::Default().IsTopWindowFullscreenOnMonitorWithMouse()) - { - return false; - } - if (hud_controller_->IsVisible()) { - ubus_manager_.SendMessage(UBUS_HUD_CLOSE_REQUEST); + hud_controller_->HideHud(); } else { + auto& wm = WindowManager::Default(); + + if (wm.IsTopWindowFullscreenOnMonitorWithMouse()) + return false; + + if (wm.IsScreenGrabbed()) + { + hud_ungrab_slot_ = wm.screen_ungrabbed.connect([this] { ShowHud(); }); + + // Let's wait ungrab event for maximum a couple of seconds... + sources_.AddTimeoutSeconds(2, [this] { + hud_ungrab_slot_->disconnect(); + return false; + }, local::HUD_UNGRAB_WAIT); + + return false; + } + // Handles closing KeyNav (Alt+F1) if the hud is about to show if (launcher_controller_->KeyNavIsActive()) launcher_controller_->KeyNavTerminate(false); - // If an overlay is open, it must be the dash! Close it! - if (launcher_controller_->IsOverlayOpen()) + if (dash_controller_->IsVisible()) dash_controller_->HideDash(); if (QuicklistManager::Default()->Current()) QuicklistManager::Default()->Current()->Hide(); + hud_ungrab_slot_->disconnect(); hud_controller_->ShowHud(); } @@ -2799,9 +2840,7 @@ bool UnityWindow::glPaint(const GLWindowPaintAttrib& attrib, * fully covers the shell on its output. It does not include regular windows * stacked above the shell like DnD icons or Onboard etc. */ - if (G_UNLIKELY(is_nux_window_) && - (!uScreen->lockscreen_controller_->IsLocked() || - uScreen->lockscreen_controller_->opacity() != 1.0f)) + if (G_UNLIKELY(is_nux_window_)) { if (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK) { @@ -2858,9 +2897,7 @@ bool UnityWindow::glPaint(const GLWindowPaintAttrib& attrib, if (uScreen->lockscreen_controller_->IsLocked()) { - if ((window->type() != CompWindowTypePopupMenuMask || - !uScreen->lockscreen_controller_->HasOpenMenu()) && - !window->minimized() && window->resName() != "onboard") + if (!window->minimized() && !CanBypassLockScreen()) { // For some reasons PAINT_WINDOW_NO_CORE_INSTANCE_MASK doesn't work here // (well, it works too much, as it applies to menus too), so we need @@ -2933,6 +2970,7 @@ bool UnityWindow::glDraw(const GLMatrix& matrix, { auto window_state = window->state(); auto window_type = window->type(); + bool locked = uScreen->lockscreen_controller_->IsLocked(); if (uScreen->doShellRepaint && !uScreen->paint_panel_under_dash_ && window_type == CompWindowTypeNormalMask) { @@ -2949,12 +2987,16 @@ bool UnityWindow::glDraw(const GLMatrix& matrix, } if (uScreen->doShellRepaint && - !uScreen->forcePaintOnTop () && window == uScreen->firstWindowAboveShell && + !uScreen->forcePaintOnTop() && !uScreen->fullscreenRegion.contains(window->geometry())) { uScreen->paintDisplay(); } + else if (locked && CanBypassLockScreen()) + { + uScreen->paintDisplay(); + } enum class DrawPanelShadow { @@ -3017,7 +3059,7 @@ bool UnityWindow::glDraw(const GLMatrix& matrix, } } - if (uScreen->lockscreen_controller_->IsLocked()) + if (locked) draw_panel_shadow = DrawPanelShadow::NO; if (draw_panel_shadow == DrawPanelShadow::BELOW_WINDOW) @@ -3819,14 +3861,14 @@ void UnityScreen::SaveLockStamp(bool save) void UnityScreen::RaiseOSK() { - /* stack any windows named "onboard" above us */ - for (CompWindow *w : screen->windows ()) + /* stack the onboard window above us */ + if (onboard_) { - if (w->resName() == "onboard") + if (nux::BaseWindow* dash = dash_controller_->window()) { - Window xid = dash_controller_->window()->GetInputWindowId(); - XSetTransientForHint (screen->dpy(), w->id(), xid); - w->raise (); + Window xid = dash->GetInputWindowId(); + XSetTransientForHint(screen->dpy(), onboard_->id(), xid); + onboard_->raise(); } } } @@ -3911,8 +3953,10 @@ void UnityScreen::initLauncher() CompOption::Value v(launcher_width); screen->setOptionForPlugin("expo", "x_offset", v); - if (launcher_controller_->options()->hide_mode != LAUNCHER_HIDE_NEVER) - screen->setOptionForPlugin("scale", "x_offset", v); + if (launcher_controller_->options()->hide_mode == LAUNCHER_HIDE_NEVER) + v.set(0); + + screen->setOptionForPlugin("scale", "x_offset", v); }; for (auto const& launcher : launcher_controller_->launchers()) @@ -4073,23 +4117,10 @@ UnityWindow::UnityWindow(CompWindow* window) if (window->state() & CompWindowStateFullscreenMask) uScreen->fullscreen_windows_.push_back(window); - /* We might be starting up so make sure that - * we don't deref the dashcontroller that doesnt - * exist */ - dash::Controller::Ptr dp = uScreen->dash_controller_; - - if (dp) + if (window->type() == CompWindowTypeUtilMask && window->resName() == "onboard") { - nux::BaseWindow* w = dp->window (); - - if (w) - { - if (window->resName() == "onboard") - { - Window xid = dp->window()->GetInputWindowId(); - XSetTransientForHint (screen->dpy(), window->id(), xid); - } - } + uScreen->onboard_ = window; + uScreen->RaiseOSK(); } } @@ -4470,6 +4501,9 @@ UnityWindow::~UnityWindow() if (window->state () & CompWindowStateFullscreenMask) uScreen->fullscreen_windows_.remove(window); + if (window == uScreen->onboard_) + uScreen->onboard_ = nullptr; + uScreen->fake_decorated_windows_.erase(this); PluginAdapter::Default().OnWindowClosed(window); } diff --git a/plugins/unityshell/src/unityshell.h b/plugins/unityshell/src/unityshell.h index 7a5cb5271..36af0514a 100644 --- a/plugins/unityshell/src/unityshell.h +++ b/plugins/unityshell/src/unityshell.h @@ -393,6 +393,7 @@ private: CompRegion nuxRegion; CompRegion fullscreenRegion; CompWindow* firstWindowAboveShell; + CompWindow* onboard_; ::GLFramebufferObject *oldFbo; @@ -421,6 +422,7 @@ private: UBusManager ubus_manager_; glib::SourceManager sources_; + connection::Wrapper hud_ungrab_slot_; CompRegion buffered_compiz_damage_this_frame_; CompRegion buffered_compiz_damage_last_frame_; @@ -519,6 +521,7 @@ private: bool IsInShowdesktopMode (); bool IsShaded (); bool IsMinimized (); + bool CanBypassLockScreen() const; void DoOverrideFrameRegion (CompRegion &r); void DoHide (); diff --git a/po/POTFILES.in b/po/POTFILES.in index 5f2c0845e..97fe6601b 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -48,5 +48,6 @@ unity-shared/CoverArt.cpp unity-shared/DashStyle.cpp unity-shared/PreviewStyle.cpp unity-shared/SearchBar.cpp +unity-shared/TextInput.cpp unity-shared/UScreen.cpp gnome/50-unity-launchers.xml.in diff --git a/resources/launcher_icon_glow_62.png b/resources/launcher_icon_glow_62.png Binary files differnew file mode 100644 index 000000000..9e21628f5 --- /dev/null +++ b/resources/launcher_icon_glow_62.png diff --git a/resources/launcher_icon_glow_62.svg b/resources/launcher_icon_glow_62.svg.save index 7eb994de3..7eb994de3 100644 --- a/resources/launcher_icon_glow_62.svg +++ b/resources/launcher_icon_glow_62.svg.save diff --git a/shutdown/SessionView.cpp b/shutdown/SessionView.cpp index 0a20f1162..8fe60d859 100644 --- a/shutdown/SessionView.cpp +++ b/shutdown/SessionView.cpp @@ -76,6 +76,7 @@ View::View(Manager::Ptr const& manager) GetBoundingArea()->mouse_click.connect([this] (int, int, unsigned long, unsigned long) { request_close.emit(); }); have_inhibitors.changed.connect(sigc::hide(sigc::mem_fun(this, &View::UpdateText))); + manager_->have_other_open_sessions.changed.connect(sigc::hide(sigc::mem_fun(this, &View::UpdateText))); mode.SetSetterFunction([this] (Mode& target, Mode new_mode) { if (new_mode == Mode::SHUTDOWN && !manager_->CanShutdown()) @@ -124,24 +125,32 @@ void View::UpdateViewSize() void View::UpdateText() { - const char* message = nullptr; + std::string message; + std::string other_users_msg; auto const& real_name = manager_->RealName(); auto const& name = (real_name.empty() ? manager_->UserName() : real_name); + other_users_msg = _("Other users are logged in. Restarting or shutting down will close their open applications and may cause them to lose work.\n\n"); + if (mode() == Mode::SHUTDOWN) { title_->SetText(_("Shut Down")); title_->SetVisible(true); + if (manager_->have_other_open_sessions()) + { + message += other_users_msg; + } + if (have_inhibitors()) { - message = _("Hi %s, you have open files that you might want to save " \ - "before shutting down. Are you sure you want to continue?"); + message += _("Hi %s, you have open files that you might want to save " \ + "before shutting down. Are you sure you want to continue?"); } else { - message = _("Goodbye, %s. Are you sure you want to close all programs " \ - "and shut down the computer?"); + message += _("Goodbye, %s. Are you sure you want to close all programs " \ + "and shut down the computer?"); } } else if (mode() == Mode::LOGOUT) @@ -164,27 +173,32 @@ void View::UpdateText() { title_->SetVisible(false); + if (manager_->have_other_open_sessions()) + { + message += other_users_msg; + } + if (have_inhibitors()) { if (buttons_layout_->GetChildren().size() > 3) { // We have enough buttons to show the message without a new line. - message = _("Hi %s, you have open files you might want to save. " \ + message += _("Hi %s, you have open files you might want to save. " \ "Would you like to…"); } else { - message = _("Hi %s, you have open files you might want to save.\n" \ + message += _("Hi %s, you have open files you might want to save.\n" \ "Would you like to…"); } } else { - message = _("Goodbye, %s. Would you like to…"); + message += _("Goodbye, %s. Would you like to…"); } } - subtitle_->SetText(glib::String(g_strdup_printf(message, name.c_str())).Str()); + subtitle_->SetText(glib::String(g_strdup_printf(message.c_str(), name.c_str())).Str()); } void View::Populate() diff --git a/tests/data/external.gschema.xml b/tests/data/external.gschema.xml index bdc418567..160db0572 100644 --- a/tests/data/external.gschema.xml +++ b/tests/data/external.gschema.xml @@ -121,4 +121,25 @@ <default>false</default> </key> </schema> + + <schema id="org.gnome.settings-daemon.plugins.media-keys" path="/org/gnome/settings-daemon/plugins/media-keys/"> + <key type="s" name="volume-mute"> + <default>'XF86AudioMute'</default> + </key> + <key type="s" name="volume-down"> + <default>'XF86AudioLowerVolume'</default> + </key> + <key type="s" name="volume-up"> + <default>'XF86AudioRaiseVolume'</default> + </key> + </schema> + + <schema id="org.gnome.desktop.wm.keybindings" path="/org/gnome/desktop/wm/keybindings/"> + <key type="as" name="switch-input-source-backward"> + <default>["<Shift><Super>space"]</default> + </key> + <key type="as" name="switch-input-source"> + <default>["<Super>space"]</default> + </key> + </schema> </schemalist> diff --git a/tests/test_lockscreen_controller.cpp b/tests/test_lockscreen_controller.cpp index 55add06bc..520f00963 100644 --- a/tests/test_lockscreen_controller.cpp +++ b/tests/test_lockscreen_controller.cpp @@ -52,16 +52,16 @@ const unsigned TICK_DURATION = 10 * 1000; struct MockShield : AbstractShield { MockShield() - : AbstractShield(nullptr, nullptr, 0, false) + : AbstractShield(nullptr, nullptr, nullptr, 0, false) {} MOCK_CONST_METHOD0(IsIndicatorOpen, bool()); - MOCK_METHOD0(CheckCapsLockPrompt, void()); + MOCK_METHOD0(ActivatePanel, void()); }; struct ShieldFactoryMock : ShieldFactoryInterface { - nux::ObjectPtr<AbstractShield> CreateShield(session::Manager::Ptr const&, indicator::Indicators::Ptr const&, int, bool) override + nux::ObjectPtr<AbstractShield> CreateShield(session::Manager::Ptr const&, indicator::Indicators::Ptr const&, Accelerators::Ptr const&, int, bool) override { return nux::ObjectPtr<AbstractShield>(new MockShield()); } diff --git a/tests/test_unity_window_view.cpp b/tests/test_unity_window_view.cpp index f9ac699ef..1eb0d2562 100644 --- a/tests/test_unity_window_view.cpp +++ b/tests/test_unity_window_view.cpp @@ -82,10 +82,10 @@ TEST_F(TestUnityWindowView, Closable) view.closable = true; ASSERT_NE(view.close_button_, nullptr); - EXPECT_EQ(view.close_button_->texture(), view.style()->GetCloseIcon()); + EXPECT_EQ(view.close_button_->texture(), view.style()->GetTexture(view.scale, WindowTextureType::CLOSE_ICON)); EXPECT_EQ(view.close_button_->GetParentObject(), &view); - int padding = view.style()->GetCloseButtonPadding(); + int padding = view.style()->GetCloseButtonPadding(view.scale); EXPECT_EQ(view.close_button_->GetBaseX(), padding); EXPECT_EQ(view.close_button_->GetBaseY(), padding); } @@ -96,16 +96,16 @@ TEST_F(TestUnityWindowView, CloseButtonStates) ASSERT_NE(view.close_button_, nullptr); view.close_button_->mouse_enter.emit(0, 0, 0, 0); - EXPECT_EQ(view.close_button_->texture(), view.style()->GetCloseIconHighligted()); + EXPECT_EQ(view.close_button_->texture(), view.style()->GetTexture(view.scale, WindowTextureType::CLOSE_ICON_HIGHLIGHTED)); view.close_button_->mouse_leave.emit(0, 0, 0, 0); - EXPECT_EQ(view.close_button_->texture(), view.style()->GetCloseIcon()); + EXPECT_EQ(view.close_button_->texture(), view.style()->GetTexture(view.scale, WindowTextureType::CLOSE_ICON)); view.close_button_->mouse_down.emit(0, 0, 0, 0); - EXPECT_EQ(view.close_button_->texture(), view.style()->GetCloseIconPressed()); + EXPECT_EQ(view.close_button_->texture(), view.style()->GetTexture(view.scale, WindowTextureType::CLOSE_ICON_PRESSED)); view.close_button_->mouse_up.emit(0, 0, 0, 0); - EXPECT_EQ(view.close_button_->texture(), view.style()->GetCloseIcon()); + EXPECT_EQ(view.close_button_->texture(), view.style()->GetTexture(view.scale, WindowTextureType::CLOSE_ICON)); } TEST_F(TestUnityWindowView, CloseButtonClicksRequestsClose) @@ -185,7 +185,7 @@ TEST_F(TestUnityWindowView, SetLayoutWrapsOriginalLayout) view.SetLayout(layout); view.ComputeContentSize(); - int offset = view.style()->GetInternalOffset(); + int offset = view.style()->GetInternalOffset(view.scale); EXPECT_EQ(layout->GetBaseX(), offset); EXPECT_EQ(layout->GetBaseY(), offset); } @@ -199,7 +199,7 @@ TEST_F(TestUnityWindowView, GetLayout) TEST_F(TestUnityWindowView, GetInternalBackground) { - int offset = view.style()->GetInternalOffset(); + int offset = view.style()->GetInternalOffset(view.scale); view.background_geo_.Set(g_random_int(), g_random_int(), g_random_int(), g_random_int()); EXPECT_EQ(view.GetInternalBackground(), view.background_geo_.GetExpand(-offset, -offset)); } @@ -240,4 +240,4 @@ TEST_F(TestUnityWindowView, FindAreaUnderMouse) } } // ui -} // unity \ No newline at end of file +} // unity diff --git a/unity-shared/DebugDBusInterface.cpp b/unity-shared/DebugDBusInterface.cpp index 4177e3fb3..59a93cc18 100644 --- a/unity-shared/DebugDBusInterface.cpp +++ b/unity-shared/DebugDBusInterface.cpp @@ -84,7 +84,8 @@ namespace local { if (!g_variant_is_of_type(prop_value, G_VARIANT_TYPE_STRING)) { - LOG_WARNING(logger) << "Unable to match '"<< name << "', it's not a string property."; + LOG_WARNING(logger) << "Unable to match '"<< name << "', '" << + prop_value << "' is not a string property."; return false; } @@ -102,7 +103,8 @@ namespace local { if (!g_variant_is_of_type(prop_value, G_VARIANT_TYPE_BOOLEAN)) { - LOG_WARNING(logger) << "Unable to match '"<< name << "', it's not a boolean property."; + LOG_WARNING(logger) << "Unable to match '"<< name << "', '" << + prop_value << "' is not a boolean property."; return false; } @@ -138,7 +140,8 @@ namespace local case G_VARIANT_CLASS_UINT64: return static_cast<uint64_t>(value) == prop_value.GetUInt64(); default: - LOG_WARNING(logger) << "Unable to match '"<< name << "' against property of unknown integer type."; + LOG_WARNING(logger) << "Unable to match '"<< name << "', '" << + prop_value << "' is not a known integer property."; }; } @@ -152,7 +155,24 @@ namespace local IntrospectionData introspection; node_->AddProperties(introspection); - return g_variant_lookup_value(glib::Variant(introspection.Get()), name.c_str(), nullptr); + + glib::Variant value(g_variant_lookup_value(glib::Variant(introspection.Get()), name.c_str(), nullptr), glib::StealRef()); + + if (!value) + return nullptr; + + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_ARRAY) || g_variant_n_children(value) != 2) + { + LOG_ERROR(logger) << "Property value for '"<< name << "' should be a 2-sized array, got instead '" << value << "'"; + return nullptr; + } + + glib::Variant child(g_variant_get_child_value(value, 1), glib::StealRef()); + + if (g_variant_is_of_type(child, G_VARIANT_TYPE_VARIANT)) + return child.GetVariant(); + + return child; } std::vector<xpathselect::Node::Ptr> Children() const diff --git a/unity-shared/PluginAdapter.cpp b/unity-shared/PluginAdapter.cpp index ddca3f118..0fffe0074 100644 --- a/unity-shared/PluginAdapter.cpp +++ b/unity-shared/PluginAdapter.cpp @@ -190,15 +190,34 @@ void PluginAdapter::NotifyCompizEvent(const char* plugin, _vp_switch_started = false; screen_viewport_switch_ended.emit(); } - else if (IsScaleActive() && g_strcmp0(plugin, "scale") == 0 && - g_strcmp0(event, "activate") == 0) + else if (g_strcmp0(plugin, "scale") == 0 && g_strcmp0(event, "activate") == 0) { - // If the scale plugin is activated again while is already grabbing the screen - // it means that is switching the view (i.e. switching from a spread application - // to another), so we need to notify our clients that it has really terminated - // and initiated again. - terminate_spread.emit(); - initiate_spread.emit(); + bool new_state = CompOption::getBoolOptionNamed(option, "active"); + + if (_spread_state != new_state) + { + _spread_state = new_state; + _spread_state ? initiate_spread.emit() : terminate_spread.emit(); + + if (!_spread_state) + _spread_windows_state = false; + } + else if (_spread_state && new_state) + { + // If the scale plugin is activated again while is already grabbing the screen + // it means that is switching the view (i.e. switching from a spread application + // to another), so we need to notify our clients that it has really terminated + // and initiated again. + + bool old_windows_state = _spread_windows_state; + _spread_state = false; + _spread_windows_state = false; + terminate_spread.emit(); + + _spread_state = true; + _spread_windows_state = old_windows_state; + initiate_spread.emit(); + } } } @@ -248,7 +267,8 @@ void MultiActionList::Initiate(std::string const& name, CompOption::Vector const argument.push_back(arg); /* Initiate the selected action with the arguments */ - action->initiate()(action, state, argument); + if (CompAction::CallBack const& initiate_cb = primary_action_->initiate()) + initiate_cb(action, 0, argument); } void MultiActionList::InitiateAll(CompOption::Vector const& extra_args, int state) const @@ -291,8 +311,11 @@ void MultiActionList::TerminateAll(CompOption::Vector const& extra_args) const if (primary_action_) { - primary_action_->terminate()(primary_action_, CompAction::StateCancel, argument); - return; + if (CompAction::CallBack const& terminate_cb = primary_action_->terminate()) + { + terminate_cb(primary_action_, CompAction::StateCancel, argument); + return; + } } for (auto const& it : actions_) @@ -304,7 +327,8 @@ void MultiActionList::TerminateAll(CompOption::Vector const& extra_args) const CompAction::StateTermEdge | CompAction::StateTermEdgeDnd)) { - action->terminate()(action, 0, argument); + if (CompAction::CallBack const& terminate_cb = primary_action_->terminate()) + terminate_cb(action, 0, argument); } } } diff --git a/unity-shared/TextInput.cpp b/unity-shared/TextInput.cpp index 2dfb72769..23a4e3196 100644 --- a/unity-shared/TextInput.cpp +++ b/unity-shared/TextInput.cpp @@ -18,6 +18,15 @@ */ #include "TextInput.h" +#include "unity-shared/IconTexture.h" +#include "unity-shared/DashStyle.h" +#include "unity-shared/RawPixel.h" +#include "unity-shared/PreviewStyle.h" + +#include <X11/XKBlib.h> + +namespace unity +{ namespace { @@ -29,6 +38,14 @@ const int TEXT_INPUT_RIGHT_BORDER = 10; const int HIGHLIGHT_HEIGHT = 24; +const RawPixel TOOLTIP_Y_OFFSET = 3_em; +const RawPixel TOOLTIP_OFFSET = 10_em; +const RawPixel DEFAULT_ICON_SIZE = 22_em; + +// Caps is on 0x1, couldn't find any #define in /usr/include/X11 +const int CAPS_STATE_ON = 0x1; + +std::string WARNING_ICON = "dialog-warning-symbolic"; // Fonts const std::string HINT_LABEL_DEFAULT_FONT_NAME = "Ubuntu"; const int HINT_LABEL_FONT_SIZE = 11; @@ -38,21 +55,44 @@ const int PANGO_ENTRY_FONT_SIZE = 14; } -namespace unity -{ - nux::logging::Logger logger("unity.textinput"); NUX_IMPLEMENT_OBJECT_TYPE(TextInput); +nux::AbstractPaintLayer* CreateWarningLayer(nux::BaseTexture* texture) +{ + // Create the texture layer + nux::TexCoordXForm texxform; + + texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); + texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_REPEAT); + texxform.min_filter = nux::TEXFILTER_LINEAR; + texxform.mag_filter = nux::TEXFILTER_LINEAR; + + nux::ROPConfig rop; + rop.Blend = true; + + rop.SrcBlend = GL_ONE; + rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA; + + return (new nux::TextureLayer(texture->GetDeviceTexture(), + texxform, + nux::color::White, + true, + rop)); +} + TextInput::TextInput(NUX_FILE_LINE_DECL) : View(NUX_FILE_LINE_PARAM) , input_hint("") , hint_font_name(HINT_LABEL_DEFAULT_FONT_NAME) , hint_font_size(HINT_LABEL_FONT_SIZE) + , show_caps_lock(false) , bg_layer_(new nux::ColorLayer(nux::Color(0xff595853), true)) + , caps_lock_on(false) , last_width_(-1) , last_height_(-1) + , mouse_over_warning_icon_(false) { layout_ = new nux::HLayout(NUX_TRACKER_LOCATION); layout_->SetLeftAndRightPadding(LEFT_INTERNAL_PADDING, TEXT_INPUT_RIGHT_BORDER); @@ -74,6 +114,7 @@ TextInput::TextInput(NUX_FILE_LINE_DECL) pango_entry_->SetFontSize(PANGO_ENTRY_FONT_SIZE); pango_entry_->cursor_moved.connect([this](int i) { QueueDraw(); }); pango_entry_->mouse_down.connect(sigc::mem_fun(this, &TextInput::OnMouseButtonDown)); + pango_entry_->key_up.connect(sigc::mem_fun(this, &TextInput::OnKeyUp)); pango_entry_->end_key_focus.connect(sigc::mem_fun(this, &TextInput::OnEndKeyFocus)); pango_entry_->text_changed.connect([this](nux::TextEntry*) { hint_->SetVisible(input_string().empty()); @@ -86,6 +127,25 @@ TextInput::TextInput(NUX_FILE_LINE_DECL) layered_layout_->SetActiveLayerN(1); layout_->AddView(layered_layout_, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FIX); + warning_ = new IconTexture(LoadWarningIcon(DEFAULT_ICON_SIZE)); + warning_->SetVisible(caps_lock_on()); + layout_->AddView(warning_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); + caps_lock_on.changed.connect([this] (bool on) { + if (show_caps_lock) + { + warning_->SetVisible(on); + QueueRelayout(); + QueueDraw(); + } + }); + + show_caps_lock.changed.connect([this] (bool changed) { + if (!warning_tooltip_.IsValid()) + LoadWarningTooltip(); + + CheckIfCapsLockOn(); + }); + spinner_ = new SearchBarSpinner(); spinner_->SetVisible(false); spinner_->SetMinMaxSize(22, 22); @@ -101,6 +161,27 @@ TextInput::TextInput(NUX_FILE_LINE_DECL) im_preedit.SetGetterFunction(sigc::mem_fun(this, &TextInput::get_im_preedit)); input_hint.changed.connect([this](std::string const& s) { OnInputHintChanged(); }); + warning_->mouse_enter.connect([this] (int x, int y, int button, int key_flags) { + mouse_over_warning_icon_ = true; + QueueDraw(); + }); + + warning_->mouse_leave.connect([this] (int x, int y, int button, int key_flags) { + mouse_over_warning_icon_ = false; + QueueDraw(); + }); +} + +void TextInput::CheckIfCapsLockOn() +{ + Display *dpy = nux::GetGraphicsDisplay()->GetX11Display(); + unsigned int state = 0; + XkbGetIndicatorState(dpy, XkbUseCoreKbd, &state); + + if ((state & CAPS_STATE_ON) == 1) + caps_lock_on = true; + else + caps_lock_on = false; } void TextInput::SetSpinnerVisible(bool visible) @@ -118,6 +199,75 @@ void TextInput::UpdateHintFont() hint_->SetFont((hint_font_name() + " " + std::to_string(hint_font_size())).c_str()); } +nux::ObjectPtr<nux::BaseTexture> TextInput::LoadWarningIcon(int icon_size) +{ + auto* theme = gtk_icon_theme_get_default(); + GtkIconLookupFlags flags = GTK_ICON_LOOKUP_FORCE_SIZE; + glib::Error error; + glib::Object<GdkPixbuf> pixbuf(gtk_icon_theme_load_icon(theme, WARNING_ICON.c_str(), icon_size, flags, &error)); + + if (pixbuf != nullptr) + { + nux::CairoGraphics cg(CAIRO_FORMAT_ARGB32, gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf)); + cairo_t* cr = cg.GetInternalContext(); + + cairo_push_group(cr); + gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0); + cairo_paint_with_alpha(cr, 1.0); + std::shared_ptr<cairo_pattern_t> pat(cairo_pop_group(cr), cairo_pattern_destroy); + + cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 1.0f); + cairo_rectangle(cr, 0, 0, gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf)); + cairo_mask(cr, pat.get()); + + return texture_ptr_from_cairo_graphics(cg); + } + // Use fallback icon (this one is a png, and does not scale!) + else + { + dash::previews::Style& preview_style = dash::previews::Style::Instance(); + return nux::ObjectPtr<nux::BaseTexture>(preview_style.GetWarningIcon()); + } +} + +void TextInput::LoadWarningTooltip() +{ + glib::String font_name; + g_object_get(gtk_settings_get_default(), "gtk-font-name", &font_name, NULL); + + glib::Object<GtkStyleContext> style_context(gtk_style_context_new()); + std::shared_ptr<GtkWidgetPath> widget_path(gtk_widget_path_new(), gtk_widget_path_free); + gtk_widget_path_append_type(widget_path.get(), GTK_TYPE_TOOLTIP); + + gtk_style_context_set_path(style_context, widget_path.get()); + gtk_style_context_add_class(style_context, "tooltip"); + + glib::Object<PangoLayout> layout; + glib::Object<PangoContext> context(gdk_pango_context_get_for_screen(gdk_screen_get_default())); + layout = pango_layout_new(context); + + std::shared_ptr<PangoFontDescription> desc(pango_font_description_from_string(font_name), pango_font_description_free); + pango_context_set_font_description(context, desc.get()); + pango_context_set_language(context, gtk_get_default_language()); + + pango_layout_set_height(layout, -1); //avoid wrap lines + pango_layout_set_text(layout, _("Caps lock is on"), -1); + + nux::Size extents; + pango_layout_get_pixel_size(layout, &extents.width, &extents.height); + extents.width += TOOLTIP_OFFSET; + extents.height += TOOLTIP_OFFSET; + + nux::CairoGraphics cg(CAIRO_FORMAT_ARGB32, extents.width, extents.height); + cairo_t* cr = cg.GetInternalContext(); + + gtk_render_background(style_context, cr, 0, 0, extents.width, extents.height); + gtk_render_frame(style_context, cr, 0, 0, extents.width, extents.height); + gtk_render_layout(style_context, cr, TOOLTIP_OFFSET/2, TOOLTIP_OFFSET/2, layout); + + warning_tooltip_ = texture_ptr_from_cairo_graphics(cg); +} + void TextInput::OnFontChanged(GtkSettings* settings, GParamSpec* pspec) { glib::String font_name; @@ -175,7 +325,6 @@ void TextInput::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw) nux::GetPainter().PushLayer(GfxContext, highlight_layer_->GetGeometry(), highlight_layer_.get()); } - if (!IsFullRedraw()) { gPainter.PushLayer(GfxContext, bg_layer_->GetGeometry(), bg_layer_.get()); @@ -187,6 +336,9 @@ void TextInput::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw) layout_->ProcessDraw(GfxContext, force_draw); + if (caps_lock_on && mouse_over_warning_icon_) + PaintWarningTooltip(GfxContext); + if (!IsFullRedraw()) { gPainter.PopBackground(); @@ -199,6 +351,18 @@ void TextInput::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw) GfxContext.PopClippingRectangle(); } +void TextInput::PaintWarningTooltip(nux::GraphicsEngine& graphics_engine) +{ + nux::Geometry warning_geo = warning_->GetGeometry(); + + nux::Geometry tooltip_geo = {warning_geo.x - (warning_tooltip_->GetWidth() + TOOLTIP_OFFSET / 2), + warning_geo.y - TOOLTIP_Y_OFFSET, + warning_tooltip_->GetWidth(), + warning_tooltip_->GetHeight()}; + + nux::GetPainter().PushDrawLayer(graphics_engine, tooltip_geo, CreateWarningLayer(warning_tooltip_.GetPointer())); +} + void TextInput::UpdateBackground(bool force) { int RADIUS = 5; @@ -254,6 +418,16 @@ void TextInput::UpdateBackground(bool force) texture2D->UnReference(); } +void TextInput::OnKeyUp(unsigned keysym, + unsigned long keycode, + unsigned long state) +{ + if (!caps_lock_on && keysym == NUX_VK_CAPITAL) + caps_lock_on = true; + else if (caps_lock_on && keysym == NUX_VK_CAPITAL) + caps_lock_on = false; +} + void TextInput::OnMouseButtonDown(int x, int y, unsigned long button, unsigned long key) { hint_->SetVisible(false); diff --git a/unity-shared/TextInput.h b/unity-shared/TextInput.h index 462474d6e..1e708511e 100644 --- a/unity-shared/TextInput.h +++ b/unity-shared/TextInput.h @@ -62,6 +62,8 @@ public: void SetSpinnerVisible(bool visible); void SetSpinnerState(SpinnerState spinner_state); + void EnableCapsLockWarning() const; + IMTextEntry* text_entry() const; nux::RWProperty<std::string> input_string; @@ -70,6 +72,7 @@ public: nux::Property<int> hint_font_size; nux::ROProperty<bool> im_active; nux::ROProperty<bool> im_preedit; + nux::Property<bool> show_caps_lock; private: void OnFontChanged(GtkSettings* settings, GParamSpec* pspec=NULL); @@ -83,9 +86,20 @@ private: void AddProperties(debug::IntrospectionData&); bool AcceptKeyNavFocus(); + bool ShouldBeHighlighted(); + + nux::Geometry GetWaringIconGeometry() const; + void CheckIfCapsLockOn(); + + nux::ObjectPtr<nux::BaseTexture> LoadWarningIcon(int icon_size); + void LoadWarningTooltip(); + + void PaintWarningTooltip(nux::GraphicsEngine& graphics_engine); + protected: void OnInputHintChanged(); void OnMouseButtonDown(int x, int y, unsigned long button_flags, unsigned long key_flags); + void OnKeyUp(unsigned keysym, unsigned long keycode, unsigned long state); void OnEndKeyFocus(); // getters & setters @@ -100,20 +114,21 @@ protected: IMTextEntry* pango_entry_; private: - - bool ShouldBeHighlighted(); - std::unique_ptr<nux::AbstractPaintLayer> bg_layer_; std::unique_ptr<nux::AbstractPaintLayer> highlight_layer_; nux::HLayout* layout_; nux::LayeredLayout* layered_layout_; SearchBarSpinner* spinner_; + nux::Property<bool> caps_lock_on; int last_width_; int last_height_; + bool mouse_over_warning_icon_; - glib::SignalManager sig_manager_; + IconTexture* warning_; + nux::ObjectPtr<nux::BaseTexture> warning_tooltip_; + glib::SignalManager sig_manager_; }; } diff --git a/unity-shared/UnityWindowStyle.cpp b/unity-shared/UnityWindowStyle.cpp index 9fbccbe75..598380a2c 100644 --- a/unity-shared/UnityWindowStyle.cpp +++ b/unity-shared/UnityWindowStyle.cpp @@ -21,9 +21,13 @@ #include <NuxCore/Logger.h> +#include "UnitySettings.h" #include "UnityWindowStyle.h" +#include "UScreen.h" #include "config.h" +#include <unordered_set> + namespace unity { namespace ui @@ -38,14 +42,31 @@ namespace const char* const DIALOG_HIGHLIGHT = PKGDATADIR"/dialog_close_highlight.png"; const char* const DIALOG_PRESS = PKGDATADIR"/dialog_close_press.png"; - double const DEFAULT_SCALE = 1.0; + + RawPixel const INTERNAL_OFFSET = 20_em; + RawPixel const BORDER_SIZE = 30_em; + RawPixel const CLOSE_PADDING = 3_em; } DECLARE_LOGGER(logger, "unity.ui.unity.window.style"); + UnityWindowStyle::UnityWindowStyle() { - LoadAllTextureInScale(DEFAULT_SCALE); + unsigned monitors = UScreen::GetDefault()->GetPluggedMonitorsNumber(); + auto& settings = Settings::Instance(); + + // Pre-load scale values per monitor + for (unsigned i = 0; i < monitors; ++i) + { + double scale = settings.Instance().em(i)->DPIScale(); + + if (unity_window_textures_.find(scale) == unity_window_textures_.end()) + LoadAllTextureInScale(scale); + } + + settings.Instance().dpi_changed.connect(sigc::mem_fun(this, &UnityWindowStyle::CleanUpUnusedTextures)); + UScreen::GetDefault()->changed.connect(sigc::mem_fun(this, &UnityWindowStyle::OnMonitorChanged)); } void UnityWindowStyle::LoadAllTextureInScale(double scale) @@ -76,6 +97,35 @@ RawPixel UnityWindowStyle::GetDefaultMaxTextureSize(const char* const texture_na return max_size; } +void UnityWindowStyle::OnMonitorChanged(int primary, std::vector<nux::Geometry> const& monitors) +{ + CleanUpUnusedTextures(); +} + +// Get current in use scale values, if a scaled value is allocated, but +// not in use clean up the scaled textures in unity_window_textures +void UnityWindowStyle::CleanUpUnusedTextures() +{ + unsigned monitors = UScreen::GetDefault()->GetPluggedMonitorsNumber(); + auto& settings = Settings::Instance(); + std::unordered_set<double> used_scales; + + for (unsigned i = 0; i < monitors; ++i) + used_scales.insert(settings.em(i)->DPIScale()); + + for (auto it = unity_window_textures_.begin(); it != unity_window_textures_.end();) + { + if (used_scales.find(it->first) == used_scales.end()) + { + it = unity_window_textures_.erase(it); + } + else + { + ++it; + } + } +} + UnityWindowStyle::Ptr const& UnityWindowStyle::Get() { // This is set only the first time; @@ -83,19 +133,19 @@ UnityWindowStyle::Ptr const& UnityWindowStyle::Get() return instance; } -int UnityWindowStyle::GetBorderSize() const +int UnityWindowStyle::GetBorderSize(double scale) const { - return 30; // as measured from textures + return BORDER_SIZE.CP(scale); // as measured from textures } -int UnityWindowStyle::GetInternalOffset() const +int UnityWindowStyle::GetInternalOffset(double scale) const { - return 20; + return INTERNAL_OFFSET.CP(scale); } -int UnityWindowStyle::GetCloseButtonPadding() const +int UnityWindowStyle::GetCloseButtonPadding(double scale) const { - return 3; + return CLOSE_PADDING.CP(scale); } UnityWindowStyle::BaseTexturePtr UnityWindowStyle::GetTexture(double scale, WindowTextureType const& type) diff --git a/unity-shared/UnityWindowStyle.h b/unity-shared/UnityWindowStyle.h index cc04f7d5e..99080a735 100644 --- a/unity-shared/UnityWindowStyle.h +++ b/unity-shared/UnityWindowStyle.h @@ -52,9 +52,9 @@ public: static UnityWindowStyle::Ptr const& Get(); BaseTexturePtr GetTexture(double scale, WindowTextureType const& type); - int GetCloseButtonPadding() const; - int GetBorderSize() const; - int GetInternalOffset() const; + int GetCloseButtonPadding(double scale) const; + int GetBorderSize(double scale) const; + int GetInternalOffset(double scale) const; private: UnityWindowStyle(); @@ -63,6 +63,9 @@ private: void LoadAllTextureInScale(double scale); nux::BaseTexture* LoadTexture(double scale, const char* const texture_name) const; RawPixel GetDefaultMaxTextureSize(const char* const texture_name) const; + + void OnMonitorChanged(int primary, std::vector<nux::Geometry> const& monitors); + void CleanUpUnusedTextures(); typedef std::array<BaseTexturePtr, size_t(WindowTextureType::Size)> UnityWindowTextures; std::unordered_map<double, UnityWindowTextures> unity_window_textures_; diff --git a/unity-shared/UnityWindowView.cpp b/unity-shared/UnityWindowView.cpp index b88a180ae..f751386f7 100644 --- a/unity-shared/UnityWindowView.cpp +++ b/unity-shared/UnityWindowView.cpp @@ -33,8 +33,7 @@ UnityWindowView::UnityWindowView(NUX_FILE_LINE_DECL) , style(UnityWindowStyle::Get()) , closable(false) , monitor(0) - , scale(1.0) - , cv_(Settings::Instance().em()) + , scale(Settings::Instance().em()->DPIScale()) , internal_layout_(nullptr) , bg_helper_(this) { @@ -49,7 +48,6 @@ UnityWindowView::UnityWindowView(NUX_FILE_LINE_DECL) }); live_background = false; - scale = cv_->DPIScale(); monitor.changed.connect(sigc::hide(sigc::mem_fun(this, &UnityWindowView::OnDPIChanged))); Settings::Instance().dpi_changed.connect(sigc::mem_fun(this, &UnityWindowView::OnDPIChanged)); @@ -68,12 +66,11 @@ UnityWindowView::~UnityWindowView() void UnityWindowView::OnDPIChanged() { - cv_ = Settings::Instance().em(monitor()); - scale = cv_->DPIScale(); + scale = Settings::Instance().em(monitor())->DPIScale(); if (internal_layout_) { - int offset = cv_->CP(style()->GetInternalOffset()); + int offset = style()->GetInternalOffset(scale); view_layout_->SetPadding(offset, offset); } } @@ -140,9 +137,8 @@ void UnityWindowView::OnClosableChanged(bool closable) return; } - auto const& texture = style()->GetTexture(cv_->DPIScale(), WindowTextureType::CLOSE_ICON); - int padding_raw = style()->GetCloseButtonPadding(); - int padding = cv_->CP(padding_raw); + auto const& texture = style()->GetTexture(scale, WindowTextureType::CLOSE_ICON); + int padding = style()->GetCloseButtonPadding(scale); close_button_ = new IconTexture(texture); close_button_->SetBaseXY(padding, padding); @@ -150,29 +146,29 @@ void UnityWindowView::OnClosableChanged(bool closable) close_button_->mouse_enter.connect([this](int, int, unsigned long, unsigned long) { if (close_button_->IsMouseOwner()) - close_button_->SetTexture(style()->GetTexture(cv_->DPIScale(), WindowTextureType::CLOSE_ICON_PRESSED)); + close_button_->SetTexture(style()->GetTexture(scale, WindowTextureType::CLOSE_ICON_PRESSED)); else - close_button_->SetTexture(style()->GetTexture(cv_->DPIScale(), WindowTextureType::CLOSE_ICON_HIGHLIGHTED)); + close_button_->SetTexture(style()->GetTexture(scale, WindowTextureType::CLOSE_ICON_HIGHLIGHTED)); }); close_button_->mouse_leave.connect([this](int, int, unsigned long, unsigned long) { - close_button_->SetTexture(style()->GetTexture(cv_->DPIScale(), WindowTextureType::CLOSE_ICON)); + close_button_->SetTexture(style()->GetTexture(scale, WindowTextureType::CLOSE_ICON)); }); close_button_->mouse_down.connect([this](int, int, unsigned long, unsigned long) { - close_button_->SetTexture(style()->GetTexture(cv_->DPIScale(), WindowTextureType::CLOSE_ICON_PRESSED)); + close_button_->SetTexture(style()->GetTexture(scale, WindowTextureType::CLOSE_ICON_PRESSED)); }); close_button_->mouse_up.connect([this](int, int, unsigned long, unsigned long) { bool inside = close_button_->IsMouseInside(); if (inside) - close_button_->SetTexture(style()->GetTexture(cv_->DPIScale(), WindowTextureType::CLOSE_ICON_HIGHLIGHTED)); + close_button_->SetTexture(style()->GetTexture(scale, WindowTextureType::CLOSE_ICON_HIGHLIGHTED)); else - close_button_->SetTexture(style()->GetTexture(cv_->DPIScale(), WindowTextureType::CLOSE_ICON)); + close_button_->SetTexture(style()->GetTexture(scale, WindowTextureType::CLOSE_ICON)); }); close_button_->mouse_click.connect([this](int, int, unsigned long, unsigned long) { - close_button_->SetTexture(style()->GetTexture(cv_->DPIScale(), WindowTextureType::CLOSE_ICON)); + close_button_->SetTexture(style()->GetTexture(scale, WindowTextureType::CLOSE_ICON)); request_close.emit(); }); @@ -183,7 +179,7 @@ bool UnityWindowView::SetLayout(nux::Layout* layout) { if (layout && layout->IsLayout()) { - int offset = cv_->CP(style()->GetInternalOffset()); + int offset = style()->GetInternalOffset(scale); // We wrap the internal layout adding some padding, so that inherited classes // can ignore the offsets we define here. @@ -208,7 +204,8 @@ nux::Layout* UnityWindowView::GetLayout() nux::Geometry UnityWindowView::GetInternalBackground() { - int offset = cv_->CP(style()->GetInternalOffset()); + int offset = style()->GetInternalOffset(scale); + return GetBackgroundGeometry().GetExpand(-offset, -offset); } @@ -358,8 +355,7 @@ void UnityWindowView::Draw(nux::GraphicsEngine& GfxContext, bool force_draw) void UnityWindowView::DrawBackground(nux::GraphicsEngine& GfxContext, nux::Geometry const& geo) { - int border = cv_->CP(style()->GetBorderSize()); - float scale = cv_->DPIScale(); + int border = style()->GetBorderSize(scale); auto background_corner_textrue = style()->GetTexture(scale, WindowTextureType::BACKGROUND_CORNER)->GetDeviceTexture(); diff --git a/unity-shared/UnityWindowView.h b/unity-shared/UnityWindowView.h index 00df54147..880390811 100644 --- a/unity-shared/UnityWindowView.h +++ b/unity-shared/UnityWindowView.h @@ -23,7 +23,6 @@ #include "unity-shared/BackgroundEffectHelper.h" #include "unity-shared/IconTexture.h" -#include "unity-shared/EMConverter.h" #include "Introspectable.h" #include "UnityWindowStyle.h" @@ -81,7 +80,6 @@ private: void OnClosableChanged(bool closable); void DrawBackground(nux::GraphicsEngine& GfxContext, nux::Geometry const& geo); - EMConverter::Ptr cv_; nux::Layout *internal_layout_; BackgroundEffectHelper bg_helper_; nux::ObjectPtr<nux::InputArea> bounding_area_; |
