diff options
| author | Marco Trevisan (Treviño) <mail@3v1n0.net> | 2016-09-02 15:36:37 +0200 |
|---|---|---|
| committer | Marco Trevisan (Treviño) <mail@3v1n0.net> | 2016-09-02 15:36:37 +0200 |
| commit | 1a8d47d4f27026ee9f7050fc6ab2bd6f1078ceec (patch) | |
| tree | bc8c8b8026a2b1f3f3336c4fcd787e3e1baa2b00 | |
| parent | 00d68bd94b4269ddfc55bdc862d41170e688a964 (diff) | |
| parent | 858e4ed48b5a2a96ef6227b36017a419b9ad69dd (diff) | |
Merging with trunk
(bzr r4153.9.53)
45 files changed, 1023 insertions, 413 deletions
diff --git a/debian/changelog b/debian/changelog index e7ebde877..c366a2dc3 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,41 @@ +unity (7.5.0+16.10.20160901.2-0ubuntu1) yakkety; urgency=medium + + [ Andrea Azzarone ] + * Disable menu discovery animation if MenusDiscoveryDuration is 0. + (LP: #942962) + * Redraw fake decorations on window resize. (LP: #940470) + + [ Marco Trevisan (Treviño) ] + * InputMonitor: add an unity class that monitors XInput2 events and + converts them to XEvent + * EdgeBarrierController: use InputMonitor to get the barrier events + instead of relying on its implementation + * DecorationsMenuLayout: use input monitor for menu scrubbing (LP: + #1614597) + * PanelView: use InputMonitor to track menu events + * LockScreenPanel: use InputMonitor events instead of mouse polling + for menu scrubbing + * MenuManager: add support for mouse trackers with triangle algorithm + support (LP: #1618405) + * PanelView: scale gradient refinement properly + * PanelService: don't allow to deactivate menus if they've been opened + too shortly + * WindowButton: properly partially unmaximize a window when + middle/left clicking in the restore button (LP: #1616136) + * CMake: move data and setting files in proper folder, define shared + libdir variables + + -- Marco Trevisan (Treviño) <mail@3v1n0.net> Thu, 01 Sep 2016 23:59:15 +0000 + +unity (7.5.0+16.10.20160819-0ubuntu1) yakkety; urgency=medium + + [ Andrea Azzarone ] + * Use compiz::Window::serverNext instead of compiz::Window::next in + IsWindowObscured as the latter can be outdated just after + scale/spread terminates. (LP: #1614116) + + -- Marco Trevisan (Treviño) <mail@3v1n0.net> Fri, 19 Aug 2016 13:15:09 +0000 + unity (7.5.0+16.10.20160817.1-0ubuntu1) yakkety; urgency=medium [ Andrea Azzarone ] diff --git a/decorations/DecoratedWindow.cpp b/decorations/DecoratedWindow.cpp index 6ded32011..fe294dc83 100644 --- a/decorations/DecoratedWindow.cpp +++ b/decorations/DecoratedWindow.cpp @@ -37,7 +37,6 @@ namespace decoration { namespace { -const std::string MENUS_PANEL_NAME = "WindowLIM"; const int SHADOW_BLUR_MARGIN_FACTOR = 2; } @@ -55,7 +54,6 @@ Window::Impl::Impl(Window* parent, CompWindow* win) , deco_elements_(cu::DecorationElement::NONE) , last_mwm_decor_(win_->mwmDecor()) , last_actions_(win_->actions()) - , panel_id_(MENUS_PANEL_NAME + std::to_string(win_->id())) , cv_(Settings::Instance().em()) { active.changed.connect([this] (bool active) { @@ -932,18 +930,13 @@ void Window::Impl::UpdateAppMenuVisibility() sliding_layout->mouse_owner = grab_edge_->mouse_owner(); } -inline std::string const& Window::Impl::GetMenusPanelID() const -{ - return panel_id_; -} - void Window::Impl::UnsetAppMenu() { if (!menus_) return; auto const& indicators = manager_->impl_->menu_manager_->Indicators(); - indicators->SyncGeometries(GetMenusPanelID(), indicator::EntryLocationMap()); + indicators->SyncGeometries(menus_->MenubarId(), indicator::EntryLocationMap()); sliding_layout_->SetInputItem(nullptr); grab_mouse_changed_->disconnect(); } @@ -956,7 +949,7 @@ void Window::Impl::SyncMenusGeometries() const auto const& indicators = manager_->impl_->menu_manager_->Indicators(); indicator::EntryLocationMap map; menus_->ChildrenGeometries(map); - indicators->SyncGeometries(GetMenusPanelID(), map); + indicators->SyncGeometries(menus_->MenubarId(), map); } bool Window::Impl::ActivateMenu(std::string const& entry_id) diff --git a/decorations/DecorationsMenuLayout.cpp b/decorations/DecorationsMenuLayout.cpp index 252fd7d0d..e31a766df 100644 --- a/decorations/DecorationsMenuLayout.cpp +++ b/decorations/DecorationsMenuLayout.cpp @@ -25,6 +25,10 @@ namespace unity { namespace decoration { +namespace +{ +const std::string MENUS_PANEL_NAME = "WindowLIM"; +} using namespace indicator; @@ -33,8 +37,8 @@ MenuLayout::MenuLayout(menu::Manager::Ptr const& menu, CompWindow* win) , show_now(false) , menu_manager_(menu) , win_(win) - , last_pointer_(-1, -1) , dropdown_(std::make_shared<MenuDropdown>(menu_manager_->Indicators(), win)) + , menubar_id_(MENUS_PANEL_NAME + std::to_string(win_->id())) { visible = false; } @@ -91,6 +95,11 @@ void MenuLayout::Setup() Relayout(); } +std::string const& MenuLayout::MenubarId() const +{ + return menubar_id_; +} + bool MenuLayout::ActivateMenu(std::string const& entry_id) { MenuEntry::Ptr target; @@ -117,14 +126,27 @@ bool MenuLayout::ActivateMenu(std::string const& entry_id) if (!activated) activated = dropdown_->ActivateChild(target); - if (activated) + return activated; +} + +bool MenuLayout::ActivateMenu(CompPoint const& pos) +{ + if (!Geometry().contains(pos)) + return false; + + for (auto const& item : items_) { - // Since this generally happens on keyboard activation we need to avoid that - // the mouse position would interfere with this - last_pointer_.set(pointerX, pointerY); + if (!item->visible() || !item->sensitive()) + continue; + + if (item->Geometry().contains(pos)) + { + std::static_pointer_cast<MenuEntry>(item)->ShowMenu(1); + return true; + } } - return activated; + return false; } void MenuLayout::OnEntryMouseOwnershipChanged(bool owner) @@ -154,39 +176,15 @@ void MenuLayout::OnEntryActiveChanged(bool actived) { active = actived; - if (active && !pointer_tracker_ && items_.size() > 1) + if (active && items_.size() > 1) { - pointer_tracker_.reset(new glib::Timeout(16)); - pointer_tracker_->Run([this] { - Window win; - int i, x, y; - unsigned int ui; - - XQueryPointer(screen->dpy(), screen->root(), &win, &win, &x, &y, &i, &i, &ui); - - if (last_pointer_.x() != x || last_pointer_.y() != y) - { - last_pointer_.set(x, y); - - for (auto const& item : items_) - { - if (!item->visible() || !item->sensitive()) - continue; - - if (item->Geometry().contains(last_pointer_)) - { - std::static_pointer_cast<MenuEntry>(item)->ShowMenu(1); - break; - } - } - } - - return true; - }); + menu_manager_->RegisterTracker(menubar_id_, (sigc::track_obj([this] (int x, int y, double speed) { + ActivateMenu(CompPoint(x, y)); + }, *this))); } else if (!active) { - pointer_tracker_.reset(); + menu_manager_->UnregisterTracker(menubar_id_); } } diff --git a/decorations/DecorationsMenuLayout.h b/decorations/DecorationsMenuLayout.h index 9fc787186..df80c3e4c 100644 --- a/decorations/DecorationsMenuLayout.h +++ b/decorations/DecorationsMenuLayout.h @@ -42,7 +42,9 @@ public: void Setup(); bool ActivateMenu(std::string const& entry_id); + bool ActivateMenu(CompPoint const&); void ChildrenGeometries(indicator::EntryLocationMap&) const; + std::string const& MenubarId() const; protected: void DoRelayout() override; @@ -55,10 +57,9 @@ private: menu::Manager::Ptr menu_manager_; CompWindow* win_; - CompPoint last_pointer_; - glib::Source::UniquePtr pointer_tracker_; glib::Source::UniquePtr show_now_timeout_; std::shared_ptr<MenuDropdown> dropdown_; + std::string menubar_id_; }; } // decoration namespace diff --git a/decorations/DecorationsPriv.h b/decorations/DecorationsPriv.h index 5199d2927..c8c5a39ec 100644 --- a/decorations/DecorationsPriv.h +++ b/decorations/DecorationsPriv.h @@ -162,7 +162,6 @@ private: connection::Wrapper dpi_changed_; connection::Wrapper grab_mouse_changed_; std::string last_title_; - std::string panel_id_; std::vector<cu::SimpleTextureQuad> bg_textures_; cu::PixmapTexture::Ptr shaped_shadow_pixmap_; std::shared_ptr<ForceQuitDialog> force_quit_; diff --git a/launcher/EdgeBarrierController.cpp b/launcher/EdgeBarrierController.cpp index a0354f585..42d509eb3 100644 --- a/launcher/EdgeBarrierController.cpp +++ b/launcher/EdgeBarrierController.cpp @@ -25,6 +25,7 @@ #include <NuxCore/Logger.h> #include "unity-shared/UnitySettings.h" #include "unity-shared/UScreen.h" +#include "unity-shared/InputMonitor.h" #include "UnityCore/GLibSource.h" namespace unity @@ -36,50 +37,10 @@ namespace { int const Y_BREAK_BUFFER = 20; int const X_BREAK_BUFFER = 20; - int const MAJOR = 2; - int const MINOR = 3; -} - -DECLARE_LOGGER(logger, "unity.edge_barrier_controller"); - -int GetXI2OpCode() -{ - Display *dpy = nux::GetGraphicsDisplay()->GetX11Display(); - - int opcode, event_base, error_base; - if (!XQueryExtension(dpy, "XFIXES", - &opcode, - &event_base, - &error_base)) - { - LOG_ERROR(logger) << "Missing XFixes"; - return -1; - } - - if (!XQueryExtension (dpy, "XInputExtension", - &opcode, - &event_base, - &error_base)) - { - LOG_ERROR(logger) << "Missing XInput"; - return -1; - } - - int maj = MAJOR; - int min = MINOR; - - if (XIQueryVersion(dpy, &maj, &min) == BadRequest) - { - LOG_ERROR(logger) << "Need XInput version 2.3"; - return -1; - } - - return opcode; } EdgeBarrierController::Impl::Impl(EdgeBarrierController *parent) - : xi2_opcode_(-1) - , edge_overcome_pressure_(0) + : edge_overcome_pressure_(0) , parent_(parent) { UScreen *uscreen = UScreen::GetDefault(); @@ -119,8 +80,6 @@ EdgeBarrierController::Impl::Impl(EdgeBarrierController *parent) options->option_changed.connect(sigc::mem_fun(this, &EdgeBarrierController::Impl::OnOptionsChanged)); SetupBarriers(UScreen::GetDefault()->GetMonitors()); }); - - xi2_opcode_ = GetXI2OpCode(); } EdgeBarrierController::Impl::~Impl() @@ -202,36 +161,30 @@ void EdgeBarrierController::Impl::ResizeBarrierList(std::vector<nux::Geometry> c } } -void SetupXI2Events() -{ - Display *dpy = nux::GetGraphicsDisplay()->GetX11Display(); - - unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; - XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; - - XISetMask(mask.mask, XI_BarrierHit); - XISetMask(mask.mask, XI_BarrierLeave); - XISelectEvents (dpy, DefaultRootWindow(dpy), &mask, 1); -} - void EdgeBarrierController::Impl::SetupBarriers(std::vector<nux::Geometry> const& layout) { if (parent_->force_disable()) return; - bool edge_resist = parent_->sticky_edges(); + size_t monitors_size = layout.size(); auto launcher_position = Settings::Instance().launcher_position(); + bool edge_resist = parent_->sticky_edges(); + bool needs_barrier = edge_resist && monitors_size > 1; + bool needs_vertical_barrier = needs_barrier; + + if (parent_->options()->hide_mode() != launcher::LauncherHideMode::LAUNCHER_HIDE_NEVER) + needs_vertical_barrier = true; - for (unsigned i = 0; i < layout.size(); i++) + for (unsigned i = 0; i < layout.size(); ++i) { - auto vertical_barrier = vertical_barriers_[i]; - auto horizontal_barrier = horizontal_barriers_[i]; - auto monitor = layout[i]; + auto const& vertical_barrier = vertical_barriers_[i]; + auto const& horizontal_barrier = horizontal_barriers_[i]; + auto const& monitor = layout[i]; vertical_barrier->DestroyBarrier(); horizontal_barrier->DestroyBarrier(); - if (edge_resist) + if (needs_barrier) { horizontal_barrier->x1 = monitor.x; horizontal_barrier->x2 = monitor.x + monitor.width; @@ -246,7 +199,7 @@ void EdgeBarrierController::Impl::SetupBarriers(std::vector<nux::Geometry> const horizontal_barrier->ConstructBarrier(); } - if (!edge_resist && parent_->options()->hide_mode() == launcher::LauncherHideMode::LAUNCHER_HIDE_NEVER) + if (!needs_vertical_barrier) continue; if (launcher_position == LauncherPosition::LEFT) @@ -273,8 +226,10 @@ void EdgeBarrierController::Impl::SetupBarriers(std::vector<nux::Geometry> const vertical_barrier->ConstructBarrier(); } - SetupXI2Events(); - AddEventFilter(); + if (needs_barrier || needs_vertical_barrier) + input::Monitor::Get().RegisterClient(input::Events::BARRIER, sigc::mem_fun(this, &Impl::HandleEvent)); + else + input::Monitor::Get().UnregisterClient(sigc::mem_fun(this, &Impl::HandleEvent)); float decay_responsiveness_mult = ((parent_->options()->edge_responsiveness() - 1) * .3f) + 1; decaymulator_.rate_of_decay = parent_->options()->edge_decay_rate() * decay_responsiveness_mult; @@ -283,65 +238,25 @@ void EdgeBarrierController::Impl::SetupBarriers(std::vector<nux::Geometry> const edge_overcome_pressure_ = parent_->options()->edge_overcome_pressure() * overcome_responsiveness_mult; } -void EdgeBarrierController::Impl::AddEventFilter() -{ - // Remove an old one, if it exists - nux::GetGraphicsDisplay()->RemoveEventFilter(this); - - nux::GraphicsDisplay::EventFilterArg event_filter; - event_filter.filter = &HandleEventCB; - event_filter.data = this; - - nux::GetGraphicsDisplay()->AddEventFilter(event_filter); -} - -bool EdgeBarrierController::Impl::HandleEvent(XEvent xevent) -{ - Display *dpy = nux::GetGraphicsDisplay()->GetX11Display(); - XGenericEventCookie *cookie = &xevent.xcookie; - bool ret = false; - - switch (cookie->evtype) - { - case (XI_BarrierHit): - { - if (XGetEventData(dpy, cookie)) - { - XIBarrierEvent* barrier_event = (XIBarrierEvent*)cookie->data; - PointerBarrierWrapper::Ptr wrapper = FindBarrierEventOwner(barrier_event); - - if (wrapper) - ret = wrapper->HandleBarrierEvent(barrier_event); - } - - XFreeEventData(dpy, cookie); - break; - } - default: - break; - } - - return ret; -} - -bool EdgeBarrierController::Impl::HandleEventCB(XEvent xevent, void* data) +void EdgeBarrierController::Impl::HandleEvent(XEvent const& xevent) { - auto edge_barrier_controller = static_cast<EdgeBarrierController::Impl*>(data); - int const xi2_opcode = edge_barrier_controller->xi2_opcode_; + if (xevent.xcookie.evtype != XI_BarrierHit) + return; - if (xevent.type != GenericEvent || xevent.xcookie.extension != xi2_opcode) - return false; + auto* barrier_event = reinterpret_cast<XIBarrierEvent*>(xevent.xcookie.data); + PointerBarrierWrapper::Ptr const& wrapper = FindBarrierEventOwner(barrier_event); - return edge_barrier_controller->HandleEvent(xevent); + if (wrapper) + wrapper->HandleBarrierEvent(barrier_event); } PointerBarrierWrapper::Ptr EdgeBarrierController::Impl::FindBarrierEventOwner(XIBarrierEvent* barrier_event) { - for (auto barrier : vertical_barriers_) + for (auto const& barrier : vertical_barriers_) if (barrier->OwnsBarrierEvent(barrier_event->barrier)) return barrier; - for (auto barrier : horizontal_barriers_) + for (auto const& barrier : horizontal_barriers_) if (barrier->OwnsBarrierEvent(barrier_event->barrier)) return barrier; diff --git a/launcher/EdgeBarrierControllerPrivate.h b/launcher/EdgeBarrierControllerPrivate.h index df852bcd0..1de19822c 100644 --- a/launcher/EdgeBarrierControllerPrivate.h +++ b/launcher/EdgeBarrierControllerPrivate.h @@ -53,12 +53,8 @@ struct EdgeBarrierController::Impl : public sigc::trackable bool EventIsInsideYBreakZone(BarrierEvent::Ptr const& event); bool EventIsInsideXBreakZone(BarrierEvent::Ptr const& event); - void AddEventFilter(); - PointerBarrierWrapper::Ptr FindBarrierEventOwner(XIBarrierEvent* barrier_event); - - static bool HandleEventCB(XEvent event, void* data); - bool HandleEvent(XEvent event); + void HandleEvent(XEvent const&); std::vector<PointerBarrierWrapper::Ptr> vertical_barriers_; std::vector<PointerBarrierWrapper::Ptr> horizontal_barriers_; @@ -68,7 +64,6 @@ struct EdgeBarrierController::Impl : public sigc::trackable Decaymulator decaymulator_; glib::Source::UniquePtr release_timeout_; - int xi2_opcode_; float edge_overcome_pressure_; EdgeBarrierController* parent_; }; diff --git a/lockscreen/KylinLockScreenShield.cpp b/lockscreen/KylinLockScreenShield.cpp index 5f2211827..2545fabb7 100644 --- a/lockscreen/KylinLockScreenShield.cpp +++ b/lockscreen/KylinLockScreenShield.cpp @@ -37,7 +37,7 @@ KylinShield::KylinShield(session::Manager::Ptr const& session_manager, Accelerators::Ptr const& accelerators, nux::ObjectPtr<AbstractUserPromptView> const& prompt_view, int monitor_num, bool is_primary) - : BaseShield(session_manager, nullptr, accelerators, prompt_view, monitor_num, is_primary) + : BaseShield(session_manager, accelerators, prompt_view, monitor_num, is_primary) { is_primary ? ShowPrimaryView() : ShowSecondaryView(); EnableInputWindow(true); diff --git a/lockscreen/LockScreenBaseShield.cpp b/lockscreen/LockScreenBaseShield.cpp index 0d65550c3..a3154c654 100644 --- a/lockscreen/LockScreenBaseShield.cpp +++ b/lockscreen/LockScreenBaseShield.cpp @@ -38,7 +38,6 @@ const unsigned MAX_GRAB_WAIT = 100; } BaseShield::BaseShield(session::Manager::Ptr const& session, - indicator::Indicators::Ptr const& indicators, Accelerators::Ptr const& accelerators, nux::ObjectPtr<AbstractUserPromptView> const& prompt_view, int monitor_num, bool is_primary) @@ -47,7 +46,6 @@ BaseShield::BaseShield(session::Manager::Ptr const& session, , monitor(monitor_num) , scale(1.0) , session_manager_(session) - , indicators_(indicators) , accelerators_(accelerators) , prompt_view_(prompt_view) , bg_settings_(std::make_shared<BackgroundSettings>()) diff --git a/lockscreen/LockScreenBaseShield.h b/lockscreen/LockScreenBaseShield.h index 06f47b21b..bf2ff0288 100644 --- a/lockscreen/LockScreenBaseShield.h +++ b/lockscreen/LockScreenBaseShield.h @@ -21,8 +21,8 @@ #define UNITY_LOCKSCREEN_BASE_SHIELD_H #include <NuxCore/Property.h> +#include "UnityCore/ConnectionManager.h" #include "UnityCore/SessionManager.h" -#include "UnityCore/Indicators.h" #include "UnityCore/GLibSource.h" #include "unity-shared/MockableBaseWindow.h" @@ -39,8 +39,8 @@ class CofView; class BaseShield : public MockableBaseWindow { public: - BaseShield(session::Manager::Ptr const&, indicator::Indicators::Ptr const&, - Accelerators::Ptr const&, nux::ObjectPtr<AbstractUserPromptView> const&, + BaseShield(session::Manager::Ptr const&, Accelerators::Ptr const&, + nux::ObjectPtr<AbstractUserPromptView> const&, int monitor_num, bool is_primary); nux::Property<bool> primary; @@ -69,7 +69,6 @@ protected: void UpdateScale(); session::Manager::Ptr session_manager_; - indicator::Indicators::Ptr indicators_; Accelerators::Ptr accelerators_; nux::ObjectPtr<AbstractUserPromptView> prompt_view_; std::shared_ptr<BackgroundSettings> bg_settings_; diff --git a/lockscreen/LockScreenController.cpp b/lockscreen/LockScreenController.cpp index d8cf9f567..10df852bc 100644 --- a/lockscreen/LockScreenController.cpp +++ b/lockscreen/LockScreenController.cpp @@ -134,7 +134,7 @@ Controller::Controller(DBusManager::Ptr const& dbus_manager, upstart_wrapper_->Emit("desktop-unlock"); systemd_wrapper_->Stop(SYSTEMD_LOCK_TARGET); accelerator_controller_.reset(); - indicators_.reset(); + menu_manager_.reset(); } else if (!prompt_activation_) { @@ -256,7 +256,7 @@ void Controller::EnsureShields(std::vector<nux::Geometry> const& monitors) if (i >= shields_size) { - shield = shield_factory_->CreateShield(session_manager_, indicators_, accelerator_controller_->GetAccelerators(), prompt_view, i, i == primary); + shield = shield_factory_->CreateShield(session_manager_, menu_manager_, accelerator_controller_->GetAccelerators(), prompt_view, i, i == primary); is_new = true; } @@ -466,7 +466,7 @@ void Controller::OnScreenSaverActivationRequest(bool activate) void Controller::LockScreen() { - indicators_ = std::make_shared<indicator::LockScreenDBusIndicators>(); + menu_manager_ = std::make_shared<menu::Manager>(std::make_shared<indicator::LockScreenDBusIndicators>(), key_grabber_); upstart_wrapper_->Emit("desktop-lock"); systemd_wrapper_->Start(SYSTEMD_LOCK_TARGET); diff --git a/lockscreen/LockScreenController.h b/lockscreen/LockScreenController.h index 8f7cbf3c5..552dba0ed 100644 --- a/lockscreen/LockScreenController.h +++ b/lockscreen/LockScreenController.h @@ -30,7 +30,6 @@ #include "SuspendInhibitorManager.h" #include "ScreenSaverDBusManager.h" #include "unity-shared/BackgroundEffectHelper.h" -#include "unity-shared/KeyGrabber.h" #include "unity-shared/SystemdWrapper.h" #include "unity-shared/UpstartWrapper.h" @@ -87,8 +86,8 @@ private: DBusManager::Ptr dbus_manager_; session::Manager::Ptr session_manager_; + menu::Manager::Ptr menu_manager_; key::Grabber::Ptr key_grabber_; - indicator::Indicators::Ptr indicators_; AcceleratorController::Ptr accelerator_controller_; SystemdWrapper::Ptr systemd_wrapper_; UpstartWrapper::Ptr upstart_wrapper_; diff --git a/lockscreen/LockScreenPanel.cpp b/lockscreen/LockScreenPanel.cpp index d2fbc8d39..168a46c6e 100644 --- a/lockscreen/LockScreenPanel.cpp +++ b/lockscreen/LockScreenPanel.cpp @@ -24,7 +24,7 @@ #include "LockScreenSettings.h" #include "panel/PanelIndicatorsView.h" -#include "unity-shared/CairoTexture.h" +#include "unity-shared/InputMonitor.h" #include "unity-shared/StaticCairoText.h" #include "unity-shared/PanelStyle.h" #include "unity-shared/RawPixel.h" @@ -38,24 +38,24 @@ namespace lockscreen namespace { const RawPixel PADDING = 5_em; +const nux::Color BG_COLOR(0.1, 0.1, 0.1, 0.4); } using namespace indicator; using namespace panel; -Panel::Panel(int monitor_, Indicators::Ptr const& indicators, session::Manager::Ptr const& session_manager) +Panel::Panel(int monitor_, menu::Manager::Ptr const& menu_manager, session::Manager::Ptr const& session_manager) : nux::View(NUX_TRACKER_LOCATION) , active(false) , monitor(monitor_) - , indicators_(indicators) + , menu_manager_(menu_manager) , needs_geo_sync_(true) { double scale = unity::Settings::Instance().em(monitor)->DPIScale(); auto* layout = new nux::HLayout(); layout->SetLeftAndRightPadding(PADDING.CP(scale), 0); SetLayout(layout); - - BuildTexture(); + UpdateSize(); // Add setting auto *hostname = new StaticCairoText(session_manager->HostName()); @@ -72,34 +72,33 @@ Panel::Panel(int monitor_, Indicators::Ptr const& indicators, session::Manager:: indicators_view_->on_indicator_updated.connect(sigc::mem_fun(this, &Panel::OnIndicatorViewUpdated)); layout->AddView(indicators_view_, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); - for (auto const& indicator : indicators_->GetIndicators()) + auto indicators = menu_manager_->Indicators(); + menu_manager_->RegisterTracker(GetPanelName(), (sigc::track_obj([this] (int x, int y, double speed) { + indicators_view_->ActivateEntryAt(x, y); + }, *this))); + + for (auto const& indicator : indicators->GetIndicators()) AddIndicator(indicator); - indicators_->on_object_added.connect(sigc::mem_fun(this, &Panel::AddIndicator)); - indicators_->on_object_removed.connect(sigc::mem_fun(this, &Panel::RemoveIndicator)); - indicators_->on_entry_show_menu.connect(sigc::mem_fun(this, &Panel::OnEntryShowMenu)); - indicators_->on_entry_activated.connect(sigc::mem_fun(this, &Panel::OnEntryActivated)); - indicators_->on_entry_activate_request.connect(sigc::mem_fun(this, &Panel::OnEntryActivateRequest)); + indicators->on_object_added.connect(sigc::mem_fun(this, &Panel::AddIndicator)); + indicators->on_object_removed.connect(sigc::mem_fun(this, &Panel::RemoveIndicator)); + indicators->on_entry_show_menu.connect(sigc::mem_fun(this, &Panel::OnEntryShowMenu)); + indicators->on_entry_activated.connect(sigc::mem_fun(this, &Panel::OnEntryActivated)); + indicators->on_entry_activate_request.connect(sigc::mem_fun(this, &Panel::OnEntryActivateRequest)); monitor.changed.connect([this, hostname] (int monitor) { double scale = unity::Settings::Instance().em(monitor)->DPIScale(); hostname->SetScale(scale); static_cast<nux::HLayout*>(GetLayout())->SetLeftAndRightPadding(PADDING.CP(scale), 0); indicators_view_->SetMonitor(monitor); - BuildTexture(); + UpdateSize(); QueueRelayout(); }); } -void Panel::BuildTexture() +void Panel::UpdateSize() { int height = panel::Style::Instance().PanelHeight(monitor); - nux::CairoGraphics context(CAIRO_FORMAT_ARGB32, 1, height); - auto* cr = context.GetInternalContext(); - cairo_set_source_rgb(cr, 0.1, 0.1, 0.1); - cairo_paint_with_alpha(cr, 0.4); - bg_texture_ = texture_ptr_from_cairo_graphics(context); - view_layout_->SetMinimumHeight(height); view_layout_->SetMaximumHeight(height); } @@ -165,12 +164,7 @@ void Panel::OnEntryShowMenu(std::string const& entry_id, unsigned xid, int x, in if (!GetInputEventSensitivity()) return; - if (!active) - { - // This is ugly... But Nux fault! - WindowManager::Default().UnGrabMousePointer(CurrentTime, button, x, y); - active = true; - } + active = true; } void Panel::OnEntryActivateRequest(std::string const& entry_id) @@ -184,36 +178,16 @@ void Panel::OnEntryActivated(std::string const& panel, std::string const& entry_ if (!GetInputEventSensitivity() || (!panel.empty() && panel != GetPanelName())) return; - bool active = !entry_id.empty(); + bool valid_entry = !entry_id.empty(); - if (active && !WindowManager::Default().IsScreenGrabbed()) + if (valid_entry && !WindowManager::Default().IsScreenGrabbed()) { // The menu didn't grab the keyboard, let's take it back. nux::GetWindowCompositor().GrabKeyboardAdd(static_cast<nux::BaseWindow*>(GetTopLevelViewWindow())); } - if (active && !track_menu_pointer_timeout_) - { - track_menu_pointer_timeout_.reset(new glib::Timeout(16)); - track_menu_pointer_timeout_->Run([this] { - nux::Point const& mouse = nux::GetGraphicsDisplay()->GetMouseScreenCoord(); - if (tracked_pointer_pos_ != mouse) - { - if (GetAbsoluteGeometry().IsPointInside(mouse.x, mouse.y)) - indicators_view_->ActivateEntryAt(mouse.x, mouse.y); - - tracked_pointer_pos_ = mouse; - } - - return true; - }); - } - else if (!active) - { - track_menu_pointer_timeout_.reset(); - tracked_pointer_pos_ = {-1, -1}; - this->active = false; - } + if (!valid_entry) + active = valid_entry; } void Panel::Draw(nux::GraphicsEngine& graphics_engine, bool force_draw) @@ -227,12 +201,7 @@ void Panel::Draw(nux::GraphicsEngine& graphics_engine, bool force_draw) graphics_engine.PushClippingRectangle(geo); nux::GetPainter().PaintBackground(graphics_engine, geo); - nux::TexCoordXForm texxform; - texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_CLAMP); - graphics_engine.QRP_1Tex(geo.x, geo.y, geo.width, geo.height, - bg_texture_->GetDeviceTexture(), texxform, - nux::color::White); - + graphics_engine.QRP_Color(geo.x, geo.y, geo.width, geo.height, BG_COLOR); view_layout_->ProcessDraw(graphics_engine, force_draw); graphics_engine.PopClippingRectangle(); @@ -242,7 +211,7 @@ void Panel::Draw(nux::GraphicsEngine& graphics_engine, bool force_draw) { EntryLocationMap locations; indicators_view_->GetGeometryForSync(locations); - indicators_->SyncGeometries(GetPanelName(), locations); + menu_manager_->Indicators()->SyncGeometries(GetPanelName(), locations); needs_geo_sync_ = false; } } diff --git a/lockscreen/LockScreenPanel.h b/lockscreen/LockScreenPanel.h index 664eccf50..cab2ae3ae 100644 --- a/lockscreen/LockScreenPanel.h +++ b/lockscreen/LockScreenPanel.h @@ -22,9 +22,9 @@ #include <Nux/Nux.h> #include <Nux/View.h> -#include "UnityCore/Indicators.h" #include "UnityCore/GLibSource.h" #include "UnityCore/SessionManager.h" +#include "unity-shared/MenuManager.h" namespace unity { @@ -39,7 +39,7 @@ namespace lockscreen class Panel : public nux::View { public: - Panel(int monitor, indicator::Indicators::Ptr const&, session::Manager::Ptr const&); + Panel(int monitor, menu::Manager::Ptr const&, session::Manager::Ptr const&); nux::Property<bool> active; nux::Property<int> monitor; @@ -55,20 +55,16 @@ private: void AddIndicator(indicator::Indicator::Ptr const&); void RemoveIndicator(indicator::Indicator::Ptr const&); void OnIndicatorViewUpdated(); - void OnEntryActivated(std::string const& panel, std::string const& entry_id, nux::Rect const& geo); + void OnEntryActivated(std::string const& panel, std::string const& entry_id, nux::Rect const&); void OnEntryShowMenu(std::string const& entry_id, unsigned xid, int x, int y, unsigned button); void OnEntryActivateRequest(std::string const& entry_id); - void BuildTexture(); + void UpdateSize(); std::string GetPanelName() const; - indicator::Indicators::Ptr indicators_; + menu::Manager::Ptr menu_manager_; panel::PanelIndicatorsView* indicators_view_; - nux::ObjectPtr<nux::BaseTexture> bg_texture_; - bool needs_geo_sync_; - nux::Point tracked_pointer_pos_; - glib::Source::UniquePtr track_menu_pointer_timeout_; }; } // lockscreen namespace diff --git a/lockscreen/LockScreenShield.cpp b/lockscreen/LockScreenShield.cpp index 264cf7132..ba6275eaa 100644 --- a/lockscreen/LockScreenShield.cpp +++ b/lockscreen/LockScreenShield.cpp @@ -32,11 +32,12 @@ namespace lockscreen { Shield::Shield(session::Manager::Ptr const& session_manager, - indicator::Indicators::Ptr const& indicators, + menu::Manager::Ptr const& menu_manager, Accelerators::Ptr const& accelerators, nux::ObjectPtr<AbstractUserPromptView> const& prompt_view, int monitor_num, bool is_primary) - : BaseShield(session_manager, indicators, accelerators, prompt_view, monitor_num, is_primary) + : BaseShield(session_manager, accelerators, prompt_view, monitor_num, is_primary) + , menu_manager_(menu_manager) , panel_view_(nullptr) { is_primary ? ShowPrimaryView() : ShowSecondaryView(); @@ -91,11 +92,11 @@ void Shield::ShowPrimaryView() Panel* Shield::CreatePanel() { - if (!indicators_ || !session_manager_) + if (!menu_manager_ || !session_manager_) return nullptr; - panel_view_ = new Panel(monitor, indicators_, session_manager_); - panel_active_conn_ = panel_view_->active.changed.connect([this] (bool active) { + panel_view_ = new Panel(monitor, menu_manager_, session_manager_); + panel_view_->active.changed.connect(sigc::track_obj([this] (bool active) { if (primary()) { if (active) @@ -109,7 +110,7 @@ Panel* Shield::CreatePanel() GrabScreen(false); } } - }); + }, *this)); return panel_view_; } diff --git a/lockscreen/LockScreenShield.h b/lockscreen/LockScreenShield.h index 053e0102f..3df1ba837 100644 --- a/lockscreen/LockScreenShield.h +++ b/lockscreen/LockScreenShield.h @@ -20,8 +20,9 @@ #ifndef UNITY_LOCKSCREEN_SHIELD_H #define UNITY_LOCKSCREEN_SHIELD_H -#include <UnityCore/ConnectionManager.h> #include "LockScreenBaseShield.h" +#include "unity-shared/MenuManager.h" + namespace unity { @@ -35,7 +36,7 @@ class Shield : public BaseShield { public: Shield(session::Manager::Ptr const&, - indicator::Indicators::Ptr const&, + menu::Manager::Ptr const&, Accelerators::Ptr const&, nux::ObjectPtr<AbstractUserPromptView> const&, int monitor, bool is_primary); @@ -50,7 +51,7 @@ private: void ShowPrimaryView() override; Panel* CreatePanel(); - connection::Wrapper panel_active_conn_; + menu::Manager::Ptr menu_manager_; Panel* panel_view_; }; diff --git a/lockscreen/LockScreenShieldFactory.cpp b/lockscreen/LockScreenShieldFactory.cpp index 4f8d51ade..45f4cccc0 100644 --- a/lockscreen/LockScreenShieldFactory.cpp +++ b/lockscreen/LockScreenShieldFactory.cpp @@ -28,7 +28,7 @@ namespace lockscreen { nux::ObjectPtr<BaseShield> ShieldFactory::CreateShield(session::Manager::Ptr const& session_manager, - indicator::Indicators::Ptr const& indicators, + menu::Manager::Ptr const& menu_manager, Accelerators::Ptr const& accelerators, nux::ObjectPtr<AbstractUserPromptView> const& prompt_view, int monitor, bool is_primary) @@ -38,7 +38,7 @@ nux::ObjectPtr<BaseShield> ShieldFactory::CreateShield(session::Manager::Ptr con if (Settings::Instance().desktop_type() == DesktopType::UBUNTUKYLIN) shield = new KylinShield(session_manager, accelerators, prompt_view, monitor, is_primary); else - shield = new Shield(session_manager, indicators, accelerators, prompt_view, monitor, is_primary); + shield = new Shield(session_manager, menu_manager, accelerators, prompt_view, monitor, is_primary); return shield; } diff --git a/lockscreen/LockScreenShieldFactory.h b/lockscreen/LockScreenShieldFactory.h index 6910e097c..de3c52968 100644 --- a/lockscreen/LockScreenShieldFactory.h +++ b/lockscreen/LockScreenShieldFactory.h @@ -22,7 +22,7 @@ #include <NuxCore/NuxCore.h> #include "UnityCore/SessionManager.h" -#include "UnityCore/Indicators.h" +#include "unity-shared/MenuManager.h" #include "LockScreenAccelerators.h" namespace unity @@ -41,7 +41,7 @@ struct ShieldFactoryInterface virtual ~ShieldFactoryInterface() = default; virtual nux::ObjectPtr<BaseShield> CreateShield(session::Manager::Ptr const&, - indicator::Indicators::Ptr const&, + menu::Manager::Ptr const&, Accelerators::Ptr const&, nux::ObjectPtr<AbstractUserPromptView> const&, int monitor, bool is_primary) = 0; @@ -50,7 +50,7 @@ struct ShieldFactoryInterface struct ShieldFactory : ShieldFactoryInterface { nux::ObjectPtr<BaseShield> CreateShield(session::Manager::Ptr const&, - indicator::Indicators::Ptr const&, + menu::Manager::Ptr const&, Accelerators::Ptr const&, nux::ObjectPtr<AbstractUserPromptView> const&, int monitor, bool is_primary) override; diff --git a/panel/PanelIndicatorEntryView.cpp b/panel/PanelIndicatorEntryView.cpp index 67a80956b..da5635c99 100644 --- a/panel/PanelIndicatorEntryView.cpp +++ b/panel/PanelIndicatorEntryView.cpp @@ -33,6 +33,8 @@ #include "unity-shared/RawPixel.h" #include "unity-shared/WindowManager.h" #include "unity-shared/ThemeSettings.h" +#include "unity-shared/UBusWrapper.h" +#include "unity-shared/UBusMessages.h" #include "unity-shared/UnitySettings.h" namespace unity @@ -117,6 +119,9 @@ void PanelIndicatorEntryView::OnMouseDown(int x, int y, long button_flags, long } else { + if (overlay_showing_) + UBusManager::SendMessage(UBUS_OVERLAY_CLOSE_REQUEST); + WindowManager& wm = WindowManager::Default(); if (wm.IsExpoActive()) @@ -140,6 +145,11 @@ void PanelIndicatorEntryView::OnMouseDown(int x, int y, long button_flags, long wm.TerminateScale(); } + // This is ugly... But Nux fault! + auto const& abs_geo = GetAbsoluteGeometry(); + guint64 timestamp = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp; + WindowManager::Default().UnGrabMousePointer(timestamp, button, abs_geo.x, abs_geo.y); + Activate(button); } } diff --git a/panel/PanelMenuView.cpp b/panel/PanelMenuView.cpp index f2aa86053..2bcdd494c 100644 --- a/panel/PanelMenuView.cpp +++ b/panel/PanelMenuView.cpp @@ -102,7 +102,6 @@ PanelMenuView::PanelMenuView(menu::Manager::Ptr const& menus) , ignore_menu_visibility_(false) , integrated_menus_(menu_manager_->integrated_menus()) , always_show_menus_(menu_manager_->always_show_menus()) - , ignore_leave_events_(false) , desktop_name_(get_current_desktop()) { if (ApplicationWindowPtr const& win = ApplicationManager::Default().GetActiveWindow()) @@ -724,7 +723,7 @@ void PanelMenuView::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw if (new_application_ && !is_inside_) { - if (opacity() != 1.0f) + if (opacity() != 1.0f && menu_manager_->discovery() > 0) StartFadeIn(menu_manager_->discovery_fadein()); } else @@ -1814,14 +1813,9 @@ void PanelMenuView::OnPanelViewMouseEnter(int x, int y, unsigned long mouse_butt } } -void PanelMenuView::IgnoreLeaveEvents(bool ignore) -{ - ignore_leave_events_ = ignore; -} - void PanelMenuView::OnPanelViewMouseLeave(int x, int y, unsigned long mouse_button_state, unsigned long special_keys_state) { - if (always_show_menus_ || ignore_leave_events_) + if (always_show_menus_) return; if (is_inside_) diff --git a/panel/PanelMenuView.h b/panel/PanelMenuView.h index 93310d10a..7d3effb84 100644 --- a/panel/PanelMenuView.h +++ b/panel/PanelMenuView.h @@ -56,7 +56,6 @@ public: bool HasKeyActivableMenus() const; void NotifyAllMenusClosed(); - void IgnoreLeaveEvents(bool); virtual void AddIndicator(indicator::Indicator::Ptr const& indicator); @@ -192,7 +191,6 @@ private: bool ignore_menu_visibility_; bool integrated_menus_; bool always_show_menus_; - bool ignore_leave_events_; nux::Geometry monitor_geo_; const std::string desktop_name_; diff --git a/panel/PanelView.cpp b/panel/PanelView.cpp index 3bf0c691e..cd08c46cd 100644 --- a/panel/PanelView.cpp +++ b/panel/PanelView.cpp @@ -42,7 +42,8 @@ namespace panel namespace { const RawPixel TRIANGLE_THRESHOLD = 5_em; -const int refine_gradient_midpoint = 959; +const double SCRUB_VELOCITY_THRESHOLD = 0.05; +const RawPixel REFINE_GRADIENT_MIDPOINT = 959; } @@ -113,10 +114,9 @@ PanelView::PanelView(MockableBaseWindow* parent, menu::Manager::Ptr const& menus remote_->on_object_added.connect(sigc::mem_fun(this, &PanelView::OnObjectAdded)); remote_->on_object_removed.connect(sigc::mem_fun(this, &PanelView::OnObjectRemoved)); - remote_->on_entry_activated.connect(sigc::mem_fun(this, &PanelView::OnEntryActivated)); - remote_->on_entry_show_menu.connect(sigc::mem_fun(this, &PanelView::OnEntryShowMenu)); menus->key_activate_entry.connect(sigc::mem_fun(this, &PanelView::ActivateEntry)); menus->open_first.connect(sigc::mem_fun(this, &PanelView::ActivateFirstSensitive)); + menus->RegisterTracker(GetPanelName(), sigc::mem_fun(this, &PanelView::OnMenuPointerMoved)); ubus_manager_.RegisterInterest(UBUS_OVERLAY_HIDDEN, sigc::mem_fun(this, &PanelView::OnOverlayHidden)); ubus_manager_.RegisterInterest(UBUS_OVERLAY_SHOWN, sigc::mem_fun(this, &PanelView::OnOverlayShown)); @@ -374,7 +374,7 @@ PanelView::Draw(nux::GraphicsEngine& GfxContext, bool force_draw) GfxContext.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); nux::TexCoordXForm refine_texxform; - int refine_x_pos = geo.x + (stored_dash_width_ - refine_gradient_midpoint); + int refine_x_pos = geo.x + (stored_dash_width_ - REFINE_GRADIENT_MIDPOINT.CP(Settings::Instance().em(monitor_))); if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) refine_x_pos += unity::Settings::Instance().LauncherSize(monitor_); @@ -473,7 +473,7 @@ PanelView::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw) nux::Geometry refine_geo = geo; - int refine_x_pos = geo.x + (stored_dash_width_ - refine_gradient_midpoint); + int refine_x_pos = geo.x + (stored_dash_width_ - REFINE_GRADIENT_MIDPOINT.CP(Settings::Instance().em(monitor_))); if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) refine_x_pos += unity::Settings::Instance().LauncherSize(monitor_); @@ -627,7 +627,7 @@ void PanelView::OnIndicatorViewUpdated() QueueDraw(); } -void PanelView::OnMenuPointerMoved(int x, int y) +void PanelView::OnMenuPointerMoved(int x, int y, double speed) { nux::Geometry const& geo = GetAbsoluteGeometry(); @@ -648,116 +648,6 @@ void PanelView::OnMenuPointerMoved(int x, int y) } } -static bool PointInTriangle(nux::Point const& p, nux::Point const& t0, nux::Point const& t1, nux::Point const& t2) -{ - int s = t0.y * t2.x - t0.x * t2.y + (t2.y - t0.y) * p.x + (t0.x - t2.x) * p.y; - int t = t0.x * t1.y - t0.y * t1.x + (t0.y - t1.y) * p.x + (t1.x - t0.x) * p.y; - - if ((s < 0) != (t < 0)) - return false; - - int A = -t1.y * t2.x + t0.y * (t2.x - t1.x) + t0.x * (t1.y - t2.y) + t1.x * t2.y; - if (A < 0) - { - s = -s; - t = -t; - A = -A; - } - - return s > 0 && t > 0 && (s + t) < A; -} - -static double GetMouseVelocity(nux::Point const& p0, nux::Point const& p1, util::Timer &timer) -{ - int dx, dy; - double speed; - auto millis = timer.ElapsedMicroSeconds(); - - if (millis == 0) - return 1; - - dx = p0.x - p1.x; - dy = p0.y - p1.y; - - speed = sqrt(dx * dx + dy * dy) / millis * 1000; - - return speed; -} - -bool PanelView::TrackMenuPointer() -{ - nux::Point const& mouse = nux::GetGraphicsDisplay()->GetMouseScreenCoord(); - double speed = GetMouseVelocity(mouse, tracked_pointer_pos_, mouse_tracker_timer_); - - mouse_tracker_timer_.Reset(); - tracked_pointer_pos_ = mouse; - - double scale = Settings::Instance().em(monitor_)->DPIScale(); - if (speed > 0 && PointInTriangle(mouse, - nux::Point(triangle_top_corner_.x, std::max(triangle_top_corner_.y - TRIANGLE_THRESHOLD.CP(scale), 0)), - nux::Point(menu_geo_.x, menu_geo_.y), - nux::Point(menu_geo_.x + menu_geo_.width, menu_geo_.y))) - { - return true; - } - - if (mouse != triangle_top_corner_) - { - triangle_top_corner_ = mouse; - OnMenuPointerMoved(mouse.x, mouse.y); - } - - return true; -} - -void PanelView::OnEntryActivated(std::string const& panel, std::string const& entry_id, nux::Rect const& menu_geo) -{ - if (!panel.empty() && panel != GetPanelName()) - return; - - menu_geo_ = menu_geo; - - bool active = !entry_id.empty(); - if (active && !track_menu_pointer_timeout_) - { - // - // Track menus being scrubbed at 60Hz (about every 16 millisec) - // It might sound ugly, but it's far nicer (and more responsive) than the - // code it replaces which used to capture motion events in another process - // (unity-panel-service) and send them to us over dbus. - // NOTE: The reason why we have to use a timer instead of tracking motion - // events is because the motion events will never be delivered to this - // process. All the motion events will go to unity-panel-service while - // scrubbing because the active panel menu has (needs) the pointer grab. - // - mouse_tracker_timer_.Reset(); - triangle_top_corner_ = nux::GetGraphicsDisplay()->GetMouseScreenCoord(); - track_menu_pointer_timeout_.reset(new glib::Timeout(16)); - track_menu_pointer_timeout_->Run(sigc::mem_fun(this, &PanelView::TrackMenuPointer)); - } - else if (!active) - { - track_menu_pointer_timeout_.reset(); - menu_view_->NotifyAllMenusClosed(); - tracked_pointer_pos_ = {-1, -1}; - } - - if (overlay_is_open_) - ubus_manager_.SendMessage(UBUS_OVERLAY_CLOSE_REQUEST); -} - -void PanelView::OnEntryShowMenu(std::string const& entry_id, unsigned xid, - int x, int y, unsigned button) -{ - if (!track_menu_pointer_timeout_) - { - // This is ugly... But Nux fault! - menu_view_->IgnoreLeaveEvents(true); - WindowManager::Default().UnGrabMousePointer(CurrentTime, button, x, y); - menu_view_->IgnoreLeaveEvents(false); - } -} - bool PanelView::ActivateFirstSensitive() { if (!IsActive()) @@ -768,7 +658,6 @@ bool PanelView::ActivateFirstSensitive() { // Since this only happens on keyboard events, we need to prevent that the // pointer tracker would select another entry. - tracked_pointer_pos_ = nux::GetGraphicsDisplay()->GetMouseScreenCoord(); return true; } @@ -785,7 +674,6 @@ bool PanelView::ActivateEntry(std::string const& entry_id) { // Since this only happens on keyboard events, we need to prevent that the // pointer tracker would select another entry. - tracked_pointer_pos_ = nux::GetGraphicsDisplay()->GetMouseScreenCoord(); return true; } diff --git a/panel/PanelView.h b/panel/PanelView.h index c25d251e5..48a6a4bed 100644 --- a/panel/PanelView.h +++ b/panel/PanelView.h @@ -35,7 +35,6 @@ #include "unity-shared/Introspectable.h" #include "unity-shared/MenuManager.h" #include "unity-shared/MockableBaseWindow.h" -#include "unity-shared/Timer.h" #include "PanelMenuView.h" #include "PanelTray.h" #include "PanelIndicatorsView.h" @@ -88,9 +87,6 @@ protected: void OnObjectAdded(indicator::Indicator::Ptr const& proxy); void OnObjectRemoved(indicator::Indicator::Ptr const& proxy); void OnIndicatorViewUpdated(); - void OnMenuPointerMoved(int x, int y); - void OnEntryActivated(std::string const& panel, std::string const& entry_id, nux::Rect const& geo); - void OnEntryShowMenu(std::string const& entry_id, unsigned xid, int x, int y, unsigned button); private: std::string GetPanelName() const; @@ -100,6 +96,8 @@ private: void OnSpreadInitiate(); void OnSpreadTerminate(); void OnLowGfxChanged(); + void OnMenuPointerMoved(int x, int y, double speed); + void OnActiveEntryEvent(XEvent const&); void EnableOverlayMode(bool); void LoadTextures(); @@ -109,7 +107,6 @@ private: bool IsTransparent(); void UpdateBackground(); void ForceUpdateBackground(); - bool TrackMenuPointer(); void SyncGeometries(); void AddPanelView(PanelIndicatorsView* child, unsigned int stretchFactor); @@ -133,10 +130,6 @@ private: BaseTexturePtr bg_refine_single_column_tex_; std::unique_ptr<nux::AbstractPaintLayer> bg_refine_single_column_layer_; - std::string active_overlay_; - nux::Point tracked_pointer_pos_, triangle_top_corner_; - util::Timer mouse_tracker_timer_; - bool is_dirty_; bool opacity_maximized_toggle_; bool needs_geo_sync_; @@ -144,15 +137,13 @@ private: float opacity_; int monitor_; int stored_dash_width_; - - nux::Geometry menu_geo_; + std::string active_overlay_; connection::Manager on_indicator_updated_connections_; connection::Manager maximized_opacity_toggle_connections_; BackgroundEffectHelper bg_effect_helper_; nux::ObjectPtr<nux::IOpenGLBaseTexture> bg_blur_texture_; UBusManager ubus_manager_; - glib::Source::UniquePtr track_menu_pointer_timeout_; }; } // namespace panel diff --git a/plugins/unityshell/src/unityshell.cpp b/plugins/unityshell/src/unityshell.cpp index 9f34cd03a..ca920df0c 100644 --- a/plugins/unityshell/src/unityshell.cpp +++ b/plugins/unityshell/src/unityshell.cpp @@ -3473,6 +3473,7 @@ void UnityWindow::moveNotify(int x, int y, bool immediate) void UnityWindow::resizeNotify(int x, int y, int w, int h) { deco_win_->UpdateDecorationPositionDelayed(); + CleanupCachedTextures(); PluginAdapter::Default().NotifyResized(window, x, y, w, h); window->resizeNotify(x, y, w, h); } diff --git a/plugins/unityshell/src/unityshell.h b/plugins/unityshell/src/unityshell.h index d11a2322a..43df66e72 100644 --- a/plugins/unityshell/src/unityshell.h +++ b/plugins/unityshell/src/unityshell.h @@ -56,6 +56,7 @@ #include "DashStyle.h" #include "EdgeBarrierController.h" #include "FavoriteStoreGSettings.h" +#include "InputMonitor.h" #include "ShortcutController.h" #include "LauncherController.h" #include "LockScreenController.h" @@ -317,6 +318,7 @@ private: internal::FavoriteStoreGSettings favorite_store_; ThumbnailGenerator thumbnail_generator_; lockscreen::Settings lockscreen_settings_; + input::Monitor input_monitor_; /* The window thread should be the last thing removed, as c++ does it in reverse order */ std::unique_ptr<nux::WindowThread> wt; diff --git a/services/panel-service.c b/services/panel-service.c index b9b86cd7f..4451fffa5 100644 --- a/services/panel-service.c +++ b/services/panel-service.c @@ -80,6 +80,7 @@ struct _PanelServicePrivate gint last_right; gint last_bottom; guint32 last_menu_button; + guint64 last_open_time; GSettings *gsettings; KeyBinding menu_toggle; @@ -92,6 +93,7 @@ struct _PanelServicePrivate /* Globals */ static gboolean suppress_signals = FALSE; +static void (*default_menu_shell_deactivate) (GtkMenuShell *menu_shell); enum { @@ -138,6 +140,7 @@ static void sort_indicators (PanelService *); static void notify_object (IndicatorObject *object); static void update_keybinding (GSettings *, const gchar *, gpointer); static void emit_upstart_event (const gchar *); +static void menu_shell_deactivate_override (GtkMenuShell *menu_shell); static gchar * get_indicator_entry_id_by_entry (IndicatorObjectEntry *entry); static IndicatorObjectEntry * get_indicator_entry_by_id (PanelService *self, const gchar *entry_id); static GdkFilterReturn event_filter (GdkXEvent *, GdkEvent *, PanelService *); @@ -161,7 +164,7 @@ panel_service_class_dispose (GObject *self) if (GTK_IS_WIDGET (priv->last_menu) && gtk_widget_get_realized (GTK_WIDGET (priv->last_menu))) { - gtk_menu_popdown (GTK_MENU (priv->last_menu)); + panel_service_close_active_entry (PANEL_SERVICE (self)); g_signal_handlers_disconnect_by_data (priv->last_menu, self); priv->last_menu = NULL; } @@ -435,6 +438,25 @@ get_indicator_entry_by_id (PanelService *self, const gchar *entry_id) return entry; } +static const gchar * +get_indicator_entry_id_by_menu (PanelService *self, GtkMenu *menu) +{ + GHashTableIter iter; + IndicatorObjectEntry* entry; + gchar *id; + + g_hash_table_iter_init (&iter, self->priv->id2entry_hash); + while (g_hash_table_iter_next (&iter, (gpointer*) &id, (gpointer*) &entry)) + { + if (entry && entry->menu == menu) + { + return id; + } + } + + return NULL; +} + static void ensure_entry_menu_is_closed (PanelService *self, const gchar *panel_id, @@ -446,7 +468,9 @@ ensure_entry_menu_is_closed (PanelService *self, if (GTK_IS_MENU (priv->last_menu) && priv->last_menu == entry->menu) { if (!priv->last_panel || !panel_id || g_strcmp0 (priv->last_panel, panel_id) == 0) - gtk_menu_popdown (entry->menu); + { + panel_service_close_active_entry (self); + } } } @@ -590,17 +614,15 @@ event_filter (GdkXEvent *ev, GdkEvent *gev, PanelService *self) event_matches_keybinding (event->mods.base, keysym, &priv->show_dash) || event_matches_keybinding (event->mods.base, keysym, &priv->show_hud)) { - if (GTK_IS_MENU (priv->last_menu)) - gtk_menu_popdown (GTK_MENU (priv->last_menu)); - + panel_service_close_active_entry (self); ret = GDK_FILTER_REMOVE; } else if (event->mods.base != GDK_CONTROL_MASK) { if (!IsModifierKey (keysym) && (event->mods.base != 0 || is_allowed_keysym (keysym))) { - if (GTK_IS_MENU (priv->last_menu) && !is_control_keysym (keysym)) - gtk_menu_popdown (GTK_MENU (priv->last_menu)); + if (!is_control_keysym (keysym)) + panel_service_close_active_entry (self); reinject_key_event_to_root_window (event); ret = GDK_FILTER_REMOVE; @@ -1370,9 +1392,7 @@ on_indicator_menu_show (IndicatorObject *object, if (!entry) { - if (GTK_IS_MENU (self->priv->last_menu)) - gtk_menu_popdown (GTK_MENU (self->priv->last_menu)); - + panel_service_close_active_entry (self); return; } @@ -1840,8 +1860,16 @@ static void on_active_menu_hidden (GtkMenu *menu, PanelService *self) { PanelServicePrivate *priv = self->priv; + GtkMenuShellClass *menu_shell_class = GTK_MENU_SHELL_GET_CLASS (priv->last_menu); + g_signal_handlers_disconnect_by_data (priv->last_menu, self); + if (menu_shell_class && default_menu_shell_deactivate && + menu_shell_class->deactivate != default_menu_shell_deactivate) + { + menu_shell_class->deactivate = default_menu_shell_deactivate; + } + priv->last_x = 0; priv->last_y = 0; priv->last_menu_button = 0; @@ -1853,6 +1881,7 @@ on_active_menu_hidden (GtkMenu *menu, PanelService *self) priv->last_right = 0; priv->last_top = 0; priv->last_bottom = 0; + priv->last_open_time = 0; priv->use_event = FALSE; priv->pressed_entry = NULL; @@ -2270,6 +2299,34 @@ menuitem_activated (GtkWidget *menuitem, IndicatorObjectEntry *entry) } static void +menu_shell_deactivate_override (GtkMenuShell *menu_shell) +{ + PanelService *self = panel_service_get_default (); + const gchar *entry_id; + + if (gtk_get_current_event () && GTK_MENU (menu_shell) == self->priv->last_menu) + { + guint64 ms_open = (g_get_monotonic_time () - self->priv->last_open_time) / 1000; + + if (ms_open < 50) + { + /* If the menu shell deactivation was requested by an event, we ensure this + * didn't happen too early to activation, or there could be a race causing + * no menu to appear. Also since the item is now marked as inactive, we should + * manually highlight it. */ + entry_id = get_indicator_entry_id_by_menu (self, self->priv->last_menu); + + if (entry_id) + g_signal_emit (self, _service_signals[ENTRY_ACTIVATE_REQUEST], 0, entry_id); + + return; + } + } + + default_menu_shell_deactivate (menu_shell); +} + +static void panel_service_show_entry_common (PanelService *self, IndicatorObject *object, IndicatorObjectEntry *entry, @@ -2281,6 +2338,7 @@ panel_service_show_entry_common (PanelService *self, { PanelServicePrivate *priv; GtkWidget *last_menu; + GtkMenuShellClass *menu_shell_class; g_return_if_fail (PANEL_IS_SERVICE (self)); g_return_if_fail (INDICATOR_IS_OBJECT (object)); @@ -2350,6 +2408,14 @@ panel_service_show_entry_common (PanelService *self, g_signal_connect_after (priv->last_menu, "move-current", G_CALLBACK (on_active_menu_move_current), self); + /* Override the menu deactivation in order to prevent it to close too early */ + menu_shell_class = GTK_MENU_SHELL_GET_CLASS (priv->last_menu); + if (menu_shell_class && menu_shell_class->deactivate != menu_shell_deactivate_override) + { + default_menu_shell_deactivate = menu_shell_class->deactivate; + menu_shell_class->deactivate = menu_shell_deactivate_override; + } + gtk_menu_shell_set_take_focus (GTK_MENU_SHELL (priv->last_menu), TRUE); gtk_menu_popup (priv->last_menu, NULL, NULL, positon_menu, self, button, CurrentTime); gboolean visible = gtk_widget_is_visible (GTK_WIDGET (priv->last_menu)); @@ -2382,6 +2448,7 @@ panel_service_show_entry_common (PanelService *self, priv->last_right = left + width -1; priv->last_top = top; priv->last_bottom = top + height -1; + priv->last_open_time = g_get_monotonic_time (); } else { @@ -2626,6 +2693,7 @@ panel_service_close_active_entry (PanelService *self) if (GTK_IS_MENU (self->priv->last_menu)) { + self->priv->last_open_time = 0; gtk_menu_popdown (GTK_MENU (self->priv->last_menu)); } } diff --git a/tests/test_edge_barrier_controller.cpp b/tests/test_edge_barrier_controller.cpp index 0aa069da4..4dae2cc80 100644 --- a/tests/test_edge_barrier_controller.cpp +++ b/tests/test_edge_barrier_controller.cpp @@ -25,6 +25,7 @@ #include "EdgeBarrierController.h" #include "EdgeBarrierControllerPrivate.h" +#include "InputMonitor.h" using namespace unity; using namespace unity::ui; @@ -108,6 +109,7 @@ public: TestBarrierSubscriber horizontal_subscribers_[monitors::MAX]; TestBarrierSubscriber vertical_subscribers_[monitors::MAX]; + input::Monitor im; MockUScreen uscreen; EdgeBarrierController bc; }; diff --git a/tests/test_launcher_controller.cpp b/tests/test_launcher_controller.cpp index 7faea517c..6a335bf8c 100644 --- a/tests/test_launcher_controller.cpp +++ b/tests/test_launcher_controller.cpp @@ -25,6 +25,7 @@ #include "ExpoLauncherIcon.h" #include "DesktopLauncherIcon.h" #include "DesktopUtilities.h" +#include "InputMonitor.h" #include "MockableBaseWindow.h" #include "MockLauncherIcon.h" #include "BFBLauncherIcon.h" @@ -286,6 +287,7 @@ protected: std::shared_ptr<helper::CaptureLogOutput> logger_output_; MockUScreen uscreen; panel::Style panel_style; + input::Monitor im_; MockFavoriteStore favorite_store; MockXdndManager::Ptr xdnd_manager_; ui::EdgeBarrierController::Ptr edge_barriers_; diff --git a/tests/test_lockscreen_controller.cpp b/tests/test_lockscreen_controller.cpp index 75d5519c9..c7db68fd2 100644 --- a/tests/test_lockscreen_controller.cpp +++ b/tests/test_lockscreen_controller.cpp @@ -55,7 +55,7 @@ const unsigned TICK_DURATION = 10 * 1000; struct MockShield : BaseShield { MockShield() - : BaseShield(nullptr, nullptr, nullptr, nux::ObjectPtr<AbstractUserPromptView>(), 0, false) + : BaseShield(nullptr, nullptr, nux::ObjectPtr<AbstractUserPromptView>(), 0, false) {} MOCK_CONST_METHOD0(IsIndicatorOpen, bool()); @@ -67,7 +67,7 @@ struct MockShield : BaseShield struct ShieldFactoryMock : ShieldFactoryInterface { nux::ObjectPtr<BaseShield> CreateShield(session::Manager::Ptr const&, - indicator::Indicators::Ptr const&, + menu::Manager::Ptr const&, Accelerators::Ptr const&, nux::ObjectPtr<AbstractUserPromptView> const&, int, bool) override diff --git a/tests/test_panel_controller.cpp b/tests/test_panel_controller.cpp index eea0f2fe1..af7fe9534 100644 --- a/tests/test_panel_controller.cpp +++ b/tests/test_panel_controller.cpp @@ -19,6 +19,7 @@ #include <gmock/gmock.h> +#include "InputMonitor.h" #include "PanelController.h" #include "PanelStyle.h" #include "PanelView.h" @@ -46,6 +47,7 @@ struct TestPanelController : public testing::Test menu::MockManager::Ptr menus; ui::EdgeBarrierController::Ptr edge_barriers; launcher::Options::Ptr options; + input::Monitor im; }; TEST_F(TestPanelController, Construction) diff --git a/tests/test_panel_view.cpp b/tests/test_panel_view.cpp index 82fdf55b3..4eeb37993 100644 --- a/tests/test_panel_view.cpp +++ b/tests/test_panel_view.cpp @@ -25,6 +25,7 @@ #include "unity-shared/PanelStyle.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/UBusWrapper.h" + #include "InputMonitor.h" #include "mock_menu_manager.h" #include "test_standalone_wm.h" @@ -43,6 +44,7 @@ public: nux::ObjectPtr<MockableBaseWindow> window_; nux::ObjectPtr<PanelView> panel_view_; testwrapper::StandaloneWM WM; + input::Monitor im; TestPanelView() : window_(new MockableBaseWindow()) diff --git a/unity-shared/CMakeLists.txt b/unity-shared/CMakeLists.txt index 216e54c25..b0ac7b12c 100644 --- a/unity-shared/CMakeLists.txt +++ b/unity-shared/CMakeLists.txt @@ -83,6 +83,7 @@ if(ENABLE_X_SUPPORT) set (UNITY_SHARED_SOURCES XKeyboardUtil.cpp XWindowManager.cpp + InputMonitor.cpp ${UNITY_SHARED_SOURCES} ) else() diff --git a/unity-shared/InputMonitor.cpp b/unity-shared/InputMonitor.cpp new file mode 100644 index 000000000..465c5afad --- /dev/null +++ b/unity-shared/InputMonitor.cpp @@ -0,0 +1,420 @@ +// -*- 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: Marco Trevisan <marco.trevisan@canonical.com> + */ + +#include "InputMonitor.h" +#include "SigcSlotHash.h" + +#include <Nux/Nux.h> +#include <NuxCore/Logger.h> +#include <X11/extensions/XInput2.h> +#include <UnityCore/GLibSource.h> +#include <unordered_set> +#include <gdk/gdkx.h> +#include <glib.h> + +namespace unity +{ +namespace input +{ +namespace +{ +DECLARE_LOGGER(logger, "unity.input.monitor"); + +Monitor* instance_ = nullptr; + +const unsigned XINPUT_MAJOR_VERSION = 2; +const unsigned XINPUT_MINOR_VERSION = 3; + +bool operator&(Events l, Events r) +{ + typedef std::underlying_type<Events>::type ut; + return static_cast<ut>(static_cast<ut>(l) & static_cast<ut>(r)); +} + +Events& operator|=(Events& l, Events r) +{ + typedef std::underlying_type<Events>::type ut; + return l = static_cast<Events>(static_cast<ut>(l) | static_cast<ut>(r)); +} + +template <typename EVENT> +void initialize_event_common(EVENT* ev, XIDeviceEvent* xiev) +{ + ev->serial = xiev->serial; + ev->send_event = xiev->send_event; + ev->display = xiev->display; + ev->window = xiev->event; + ev->root = xiev->root; + ev->subwindow = xiev->child; + ev->time = xiev->time; + ev->x = std::round(xiev->event_x); + ev->y = std::round(xiev->event_y); + ev->x_root = std::round(xiev->root_x); + ev->y_root = std::round(xiev->root_y); + ev->state = xiev->mods.effective; + ev->same_screen = True; +} + +template <typename EVENT_TYPE, typename NATIVE_TYPE> +void initialize_event(XEvent* ev, NATIVE_TYPE* xiev); + +template <> +void initialize_event<XButtonEvent>(XEvent* ev, XIDeviceEvent* xiev) +{ + XButtonEvent* bev = &ev->xbutton; + ev->type = (xiev->evtype == XI_ButtonPress) ? ButtonPress : ButtonRelease; + initialize_event_common(bev, xiev); + bev->button = xiev->detail; +} + +template <> +void initialize_event<XKeyEvent>(XEvent* ev, XIDeviceEvent* xiev) +{ + XKeyEvent* kev = &ev->xkey; + ev->type = (xiev->evtype == XI_KeyPress) ? KeyPress : KeyRelease; + initialize_event_common(kev, xiev); + kev->keycode = xiev->detail; +} + +template <> +void initialize_event<XMotionEvent>(XEvent* ev, XIDeviceEvent* xiev) +{ + XMotionEvent* mev = &ev->xmotion; + ev->type = MotionNotify; + initialize_event_common(mev, xiev); + mev->is_hint = NotifyNormal; + + for (int i = 0; i < xiev->buttons.mask_len * 8; ++i) + { + if (XIMaskIsSet(xiev->buttons.mask, i)) + { + mev->is_hint = NotifyHint; + break; + } + } +} + +template <> +void initialize_event<XGenericEventCookie>(XEvent* ev, XIBarrierEvent* xiev) +{ + XGenericEventCookie* cev = &ev->xcookie; + cev->type = GenericEvent; + cev->serial = xiev->serial; + cev->send_event = xiev->send_event; + cev->display = xiev->display; + cev->evtype = xiev->evtype; + cev->data = xiev; +} +} + +struct Monitor::Impl +{ +#if __GNUC__ < 6 + using EventCallbackSet = std::unordered_set<EventCallback>; +#else + using EventCallbackSet = std::unordered_set<EventCallback, std::hash<sigc::slot_base>>; +#endif + + Impl() + : xi_opcode_(0) + , event_filter_set_(false) + , invoking_callbacks_(false) + { + Display *dpy = gdk_x11_get_default_xdisplay(); + int event_base, error_base; + + if (XQueryExtension(dpy, "XInputExtension", &xi_opcode_, &event_base, &error_base)) + { + int maj = XINPUT_MAJOR_VERSION; + int min = XINPUT_MINOR_VERSION; + + if (XIQueryVersion(dpy, &maj, &min) == BadRequest) + { + LOG_ERROR(logger) << "Need XInput version "<< maj << "." << min << ", " + << "impossible, to setup an InputMonitor"; + } + } + else + { + LOG_ERROR(logger) << "Missing XInput, impossible to setup an InputMonitor"; + } + } + + ~Impl() + { + if (event_filter_set_) + { + pointer_callbacks_.clear(); + key_callbacks_.clear(); + barrier_callbacks_.clear(); + UpdateEventMonitor(); + } + } + + bool RegisterClient(Events type, EventCallback const& cb) + { + bool added = false; + + if (type & Events::POINTER) + added = pointer_callbacks_.insert(cb).second || added; + + if (type & Events::KEYS) + added = key_callbacks_.insert(cb).second || added; + + if (type & Events::BARRIER) + added = barrier_callbacks_.insert(cb).second || added; + + if (added) + UpdateEventMonitor(); + + return added; + } + + bool UnregisterClient(EventCallback const& cb) + { + if (invoking_callbacks_) + { + // Delay the event removal if we're currently invoking a callback + // not to break the callbacks loop + removal_queue_.insert(cb); + return false; + } + + bool removed = false; + removed = pointer_callbacks_.erase(cb) > 0 || removed; + removed = key_callbacks_.erase(cb) > 0 || removed; + removed = barrier_callbacks_.erase(cb) > 0 || removed; + + if (removed) + UpdateEventMonitor(); + + return removed; + } + + Events RegisteredEvents(EventCallback const& cb) const + { + Events events = Events::NONE; + + if (pointer_callbacks_.find(cb) != end(pointer_callbacks_)) + events |= Events::POINTER; + + if (key_callbacks_.find(cb) != end(key_callbacks_)) + events |= Events::KEYS; + + if (barrier_callbacks_.find(cb) != end(barrier_callbacks_)) + events |= Events::BARRIER; + + return events; + } + + void UpdateEventMonitor() + { + auto* dpy = nux::GetGraphicsDisplay()->GetX11Display(); + Window root = DefaultRootWindow(dpy); + + unsigned char master_dev_bits[XIMaskLen(XI_LASTEVENT)] = { 0 }; + XIEventMask master_dev = { XIAllMasterDevices, sizeof(master_dev_bits), master_dev_bits }; + + if (!barrier_callbacks_.empty()) + { + XISetMask(master_dev.mask, XI_BarrierHit); + XISetMask(master_dev.mask, XI_BarrierLeave); + } + + unsigned char all_devs_bits[XIMaskLen(XI_LASTEVENT)] = { 0 }; + XIEventMask all_devs = { XIAllDevices, sizeof(all_devs_bits), all_devs_bits }; + + if (!pointer_callbacks_.empty()) + { + XISetMask(all_devs.mask, XI_Motion); + XISetMask(all_devs.mask, XI_ButtonPress); + XISetMask(all_devs.mask, XI_ButtonRelease); + } + + if (!key_callbacks_.empty()) + { + XISetMask(all_devs.mask, XI_KeyPress); + XISetMask(all_devs.mask, XI_KeyRelease); + } + + XIEventMask selected[] = {master_dev, all_devs}; + XISelectEvents(dpy, root, selected, G_N_ELEMENTS(selected)); + XSync(dpy, False); + + LOG_DEBUG(logger) << "Pointer clients: " << pointer_callbacks_.size() << ", " + << "Key clients: " << key_callbacks_.size() << ", " + << "Barrier clients: " << barrier_callbacks_.size(); + + if (!pointer_callbacks_.empty() || !key_callbacks_.empty() || !barrier_callbacks_.empty()) + { + if (!event_filter_set_) + { + nux::GetGraphicsDisplay()->AddEventFilter({[] (XEvent event, void* data) { + return static_cast<Impl*>(data)->HandleEvent(event); + }, this}); + + event_filter_set_ = true; + LOG_DEBUG(logger) << "Event filter enabled"; + } + } + else if (event_filter_set_) + { + nux::GetGraphicsDisplay()->RemoveEventFilter(this); + event_filter_set_ = false; + LOG_DEBUG(logger) << "Event filter disabled"; + } + } + + bool HandleEvent(XEvent& event) + { + bool handled = false; + + if (event.type != GenericEvent || event.xcookie.extension != xi_opcode_) + return handled; + + switch (event.xcookie.evtype) + { + case XI_ButtonPress: + case XI_ButtonRelease: + handled = InvokeCallbacks<XButtonEvent>(pointer_callbacks_, event); + break; + case XI_Motion: + handled = InvokeCallbacks<XMotionEvent>(pointer_callbacks_, event); + break; + case XI_KeyPress: + case XI_KeyRelease: + handled = InvokeCallbacks<XKeyEvent>(key_callbacks_, event); + break; + case XI_BarrierHit: + case XI_BarrierLeave: + handled = InvokeCallbacks<XGenericEventCookie, XIBarrierEvent>(barrier_callbacks_, event); + break; + } + + return handled; + } + + template <typename EVENT_TYPE, typename NATIVE_TYPE = XIDeviceEvent> + bool InvokeCallbacks(EventCallbackSet& callbacks, XEvent& xiev) + { + XGenericEventCookie *cookie = &xiev.xcookie; + + if (!XGetEventData(xiev.xany.display, cookie)) + return false; + + XEvent event; + initialize_event<EVENT_TYPE>(&event, reinterpret_cast<NATIVE_TYPE*>(cookie->data)); + invoking_callbacks_ = true; + + for (auto it = callbacks.begin(); it != callbacks.end();) + { + if (it->empty()) + { + it = callbacks.erase(it); + continue; + } + + (*it)(event); + ++it; + } + + XFreeEventData(xiev.xany.display, cookie); + invoking_callbacks_ = false; + + // A callback might unregister itself on the event callback, causing the + // above callbacks loop to crash, so in this case we save the event in the + // removal queue and eventually we unregistered these callbacks. + bool update_event_monitor = false; + for (auto it = removal_queue_.begin(); it != removal_queue_.end(); it = removal_queue_.erase(it)) + { + auto const& cb = *it; + pointer_callbacks_.erase(cb); + key_callbacks_.erase(cb); + barrier_callbacks_.erase(cb); + update_event_monitor = true; + } + + if (callbacks.empty() || update_event_monitor) + { + idle_removal_.reset(new glib::Idle([this] { + UpdateEventMonitor(); + return false; + })); + + return false; + } + + return true; + } + + int xi_opcode_; + bool event_filter_set_; + bool invoking_callbacks_; + glib::Source::UniquePtr idle_removal_; + EventCallbackSet pointer_callbacks_; + EventCallbackSet key_callbacks_; + EventCallbackSet barrier_callbacks_; + EventCallbackSet removal_queue_; +}; + +Monitor::Monitor() +{ + if (instance_) + { + LOG_WARN(logger) << "More than one input::Monitor created."; + return; + } + + instance_ = this; + impl_.reset(new Impl()); +} + +Monitor::~Monitor() +{ + if (this == instance_) + instance_ = nullptr; +} + +Monitor& Monitor::Get() +{ + if (!instance_) + { + LOG_ERROR(logger) << "No input::Monitor created yet."; + } + + return *instance_; +} + +bool Monitor::RegisterClient(Events events, EventCallback const& cb) +{ + return impl_->RegisterClient(events, cb); +} + +bool Monitor::UnregisterClient(EventCallback const& cb) +{ + return impl_->UnregisterClient(cb); +} + +Events Monitor::RegisteredEvents(EventCallback const& cb) const +{ + return impl_->RegisteredEvents(cb); +} + +} // input namespace +} // unity namespace diff --git a/unity-shared/InputMonitor.h b/unity-shared/InputMonitor.h new file mode 100644 index 000000000..23ff8ceff --- /dev/null +++ b/unity-shared/InputMonitor.h @@ -0,0 +1,67 @@ +// -*- 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: Marco Trevisan <marco.trevisan@canonical.com> + */ + +#ifndef __UNITY_INPUT_MONITOR__ +#define __UNITY_INPUT_MONITOR__ + +#include <X11/Xlib.h> +#include <sigc++/slot.h> +#include <memory> + +namespace unity +{ +namespace input +{ +enum class Events : unsigned +{ + NONE = 0, + POINTER = (1 << 0), + KEYS = (1 << 1), + BARRIER = (1 << 2), + INPUT = POINTER | KEYS, + ALL = POINTER | KEYS | BARRIER +}; + +class Monitor : public sigc::trackable +{ +public: + typedef sigc::slot<void, XEvent const&> EventCallback; + + static Monitor& Get(); + + Monitor(); + ~Monitor(); + + bool RegisterClient(Events, EventCallback const&); + bool UnregisterClient(EventCallback const&); + + Events RegisteredEvents(EventCallback const&) const; + +private: + Monitor(Monitor const&) = delete; + Monitor& operator=(Monitor const&) = delete; + + struct Impl; + std::unique_ptr<Impl> impl_; +}; + +} // input namespace +} // unity namespace + +#endif // __UNITY_INPUT_MONITOR__ diff --git a/unity-shared/MenuManager.cpp b/unity-shared/MenuManager.cpp index f00887ce8..77828700d 100644 --- a/unity-shared/MenuManager.cpp +++ b/unity-shared/MenuManager.cpp @@ -21,11 +21,17 @@ #include <gtk/gtk.h> #include <NuxCore/Logger.h> #include <UnityCore/GLibSignal.h> +#include <UnityCore/GLibSource.h> #include <UnityCore/GLibWrapper.h> #include <UnityCore/DBusIndicators.h> #include <unordered_map> #include "MenuManager.h" +#include "InputMonitor.h" +#include "RawPixel.h" +#include "SigcSlotHash.h" +#include "UnitySettings.h" +#include "UScreen.h" #include "WindowManager.h" namespace unity @@ -40,6 +46,10 @@ const std::string SETTINGS_NAME = "com.canonical.Unity"; const std::string LIM_KEY = "integrated-menus"; const std::string SHOW_MENUS_NOW_DELAY = "show-menus-now-delay"; const std::string ALWAYS_SHOW_MENUS_KEY = "always-show-menus"; + +const RawPixel TRIANGLE_THRESHOLD = 5_em; +const double SCRUB_VELOCITY_THRESHOLD = 0.05; +const unsigned MENU_OPEN_MOUSE_WAIT = 150; } using namespace indicator; @@ -51,6 +61,7 @@ struct Manager::Impl : sigc::trackable , indicators_(indicators) , key_grabber_(grabber) , show_now_window_(0) + , last_pointer_time_(0) , settings_(g_settings_new(SETTINGS_NAME.c_str())) { for (auto const& indicator : indicators_->GetIndicators()) @@ -182,9 +193,15 @@ struct Manager::Impl : sigc::trackable parent_->key_activate_entry.emit(entry_id); } - void EntryActivated(std::string const&, std::string const&, nux::Rect const& geo) + void EntryActivated(std::string const& menubar, std::string const&, nux::Rect const& geo) { parent_->menu_open = !geo.IsNull(); + + if (active_menubar_ != menubar) + { + active_menubar_ = menubar; + UpdateActiveTracker(); + } } void SetShowNowForWindow(Window xid, bool show) @@ -231,15 +248,148 @@ struct Manager::Impl : sigc::trackable gtk_icon_theme_set_search_path(gtk_icon_theme_get_default(), gicon_paths.data(), gicon_paths.size()); } + bool PointInTriangle(nux::Point const& p, nux::Point const& t0, nux::Point const& t1, nux::Point const& t2) + { + int s = t0.y * t2.x - t0.x * t2.y + (t2.y - t0.y) * p.x + (t0.x - t2.x) * p.y; + int t = t0.x * t1.y - t0.y * t1.x + (t0.y - t1.y) * p.x + (t1.x - t0.x) * p.y; + + if ((s < 0) != (t < 0)) + return false; + + int A = -t1.y * t2.x + t0.y * (t2.x - t1.x) + t0.x * (t1.y - t2.y) + t1.x * t2.y; + if (A < 0) + { + s = -s; + t = -t; + A = -A; + } + + return s > 0 && t > 0 && (s + t) < A; + } + + double GetMouseVelocity(nux::Point const& p0, nux::Point const& p1, Time time_delta) + { + int dx, dy; + double speed; + + if (time_delta == 0) + return 1; + + dx = p0.x - p1.x; + dy = p0.y - p1.y; + + speed = sqrt(dx * dx + dy * dy) / time_delta; + + return speed; + } + + void OnActiveEntryEvent(XEvent const& e) + { + if (e.type != MotionNotify) + return; + + auto const& active_entry = indicators_->GetActiveEntry(); + + if (!active_entry) + return; + + nux::Point mouse(e.xmotion.x_root, e.xmotion.y_root); + auto monitor = UScreen::GetDefault()->GetMonitorAtPosition(mouse.x, mouse.y); + double scale = Settings::Instance().em(monitor)->DPIScale(); + double speed = GetMouseVelocity(mouse, tracked_pointer_pos_, e.xmotion.time - last_pointer_time_); + auto menu_geo = active_entry->geometry(); + + tracked_pointer_pos_ = mouse; + last_pointer_time_ = e.xmotion.time; + + if (speed > SCRUB_VELOCITY_THRESHOLD && + PointInTriangle(mouse, {mouse.x, std::max(mouse.y - TRIANGLE_THRESHOLD.CP(scale), 0)}, + menu_geo.GetPosition(), {menu_geo.x + menu_geo.width, menu_geo.y})) + { + pointer_movement_timeout_ = std::make_shared<glib::Timeout>(MENU_OPEN_MOUSE_WAIT, [this, mouse, speed] { + if (active_tracker_) + active_tracker_(mouse.x, mouse.y, speed); + + return false; + }); + + return; + } + + if (active_tracker_) + { + pointer_movement_timeout_.reset(); + active_tracker_(mouse.x, mouse.y, speed); + } + } + + bool RegisterTracker(std::string const& menubar, PositionTracker const& cb) + { + auto it = position_trackers_.find(menubar); + + if (it != end(position_trackers_)) + return false; + + position_trackers_.insert({menubar, cb}); + + if (active_menubar_ == menubar) + UpdateActiveTracker(); + + return true; + } + + bool UnregisterTracker(std::string const& menubar, PositionTracker const& cb) + { + auto it = position_trackers_.find(menubar); + + if (it == end(position_trackers_)) + return false; + + if (!cb || (cb && it->second == cb)) + { + position_trackers_.erase(it); + UpdateActiveTracker(); + return true; + } + + return false; + } + + void UpdateActiveTracker() + { + auto it = position_trackers_.find(active_menubar_); + active_tracker_ = (it != end(position_trackers_)) ? it->second : PositionTracker(); + pointer_movement_timeout_.reset(); + + if (active_tracker_) + { + if (input::Monitor::Get().RegisterClient(input::Events::POINTER, sigc::mem_fun(this, &Impl::OnActiveEntryEvent))) + last_pointer_time_ = 0; + } + else + { + input::Monitor::Get().UnregisterClient(sigc::mem_fun(this, &Impl::OnActiveEntryEvent)); + + if (it != end(position_trackers_)) + position_trackers_.erase(it); + } + } + Manager* parent_; Indicators::Ptr indicators_; AppmenuIndicator::Ptr appmenu_; key::Grabber::Ptr key_grabber_; Window show_now_window_; + std::string active_menubar_; + PositionTracker active_tracker_; + nux::Point tracked_pointer_pos_; + Time last_pointer_time_; + glib::Source::Ptr pointer_movement_timeout_; connection::Manager appmenu_connections_; connection::Wrapper active_win_conn_; glib::Object<GSettings> settings_; glib::SignalManager signals_; + std::unordered_map<std::string, PositionTracker> position_trackers_; std::unordered_map<indicator::Entry::Ptr, uint32_t> entry_actions_; }; @@ -278,5 +428,16 @@ key::Grabber::Ptr const& Manager::KeyGrabber() const return impl_->key_grabber_; } +bool Manager::RegisterTracker(std::string const& menubar, PositionTracker const& cb) +{ + return impl_->RegisterTracker(menubar, cb); +} + +bool Manager::UnregisterTracker(std::string const& menubar, PositionTracker const& cb) +{ + return impl_->UnregisterTracker(menubar, cb); +} + + } // menu namespace } // unity namespace diff --git a/unity-shared/MenuManager.h b/unity-shared/MenuManager.h index 20432db73..ad0f177e2 100644 --- a/unity-shared/MenuManager.h +++ b/unity-shared/MenuManager.h @@ -67,6 +67,10 @@ public: key::Grabber::Ptr const& KeyGrabber() const; + typedef sigc::slot<void, int /*x*/, int /*y*/, double /*speed*/> PositionTracker; + bool RegisterTracker(std::string const& menubar, PositionTracker const&); + bool UnregisterTracker(std::string const& menubar, PositionTracker const& = PositionTracker()); + sigc::signal<void> appmenu_added; sigc::signal<void> appmenu_removed; sigc::signal<bool>::accumulated<any_true> open_first; diff --git a/unity-shared/PluginAdapter.cpp b/unity-shared/PluginAdapter.cpp index e439ca332..324bf54b1 100644 --- a/unity-shared/PluginAdapter.cpp +++ b/unity-shared/PluginAdapter.cpp @@ -608,7 +608,7 @@ bool PluginAdapter::IsWindowObscured(Window window_id) const CompPoint window_vp = window->defaultViewport(); // Check if any windows above this one are blocking it - for (CompWindow* sibling = window->next; sibling != NULL; sibling = sibling->next) + for (CompWindow* sibling = window->serverNext; sibling != NULL; sibling = sibling->serverNext) { if (sibling->defaultViewport() == window_vp && !sibling->minimized() diff --git a/unity-shared/SigcSlotHash.h b/unity-shared/SigcSlotHash.h new file mode 100644 index 000000000..c7058c3b8 --- /dev/null +++ b/unity-shared/SigcSlotHash.h @@ -0,0 +1,70 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* + * Copyright (C) 2016 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: Marco Trevisan <marco.trevisan@canonical.com> + */ + +#ifndef __UNITY_SIGC_SLOT_HASHER__ +#define __UNITY_SIGC_SLOT_HASHER__ + +#include <sigc++/slot.h> + +namespace sigc +{ +inline bool operator==(slot_base const& lhs, slot_base const& rhs) +{ + if (!lhs.rep_ || !rhs.rep_) + return (lhs.rep_ == rhs.rep_); + + return (lhs.rep_->call_ == rhs.rep_->call_); +} + +inline bool operator!=(slot_base const& lhs, slot_base const& rhs) +{ + return !(lhs == rhs); +} +} // sigc namespace + +namespace std +{ + +template<> +struct hash<sigc::slot_base> +{ + size_t operator()(sigc::slot_base const& cb) const + { + if (cb.rep_) + return hash<size_t>()(reinterpret_cast<size_t>(cb.rep_->call_)); + + return hash<size_t>()(reinterpret_cast<size_t>(cb.rep_)); + } +}; + +#if __GNUC__ < 6 +template<class T> +struct hash +{ + size_t operator()(T const& cb) const + { + static_assert(std::is_base_of<sigc::slot_base, T>::value, "Type is not derived from sigc::slot_base"); + return hash<sigc::slot_base>()(cb); + } +}; +#endif + +} // std namespace + +#endif // __UNITY_SIGC_SLOT_HASHER__ diff --git a/unity-shared/StandaloneWindowManager.cpp b/unity-shared/StandaloneWindowManager.cpp index 44aa839d6..43eef13c7 100644 --- a/unity-shared/StandaloneWindowManager.cpp +++ b/unity-shared/StandaloneWindowManager.cpp @@ -624,6 +624,9 @@ std::string StandaloneWindowManager::GetStringProperty(Window, Atom) const return std::string(); } +void StandaloneWindowManager::SetCardinalProperty(Window, Atom, std::vector<long> const&) +{} + std::vector<long> StandaloneWindowManager::GetCardinalProperty(Window, Atom) const { return std::vector<long>(); diff --git a/unity-shared/StandaloneWindowManager.h b/unity-shared/StandaloneWindowManager.h index 8b2775233..60bb78824 100644 --- a/unity-shared/StandaloneWindowManager.h +++ b/unity-shared/StandaloneWindowManager.h @@ -165,6 +165,7 @@ public: virtual std::string GetWindowName(Window window_id) const; virtual bool IsOnscreenKeyboard(Window window_id) const; virtual std::string GetStringProperty(Window window_id, Atom) const; + virtual void SetCardinalProperty(Window window_id, Atom, std::vector<long> const&); virtual std::vector<long> GetCardinalProperty(Window window_id, Atom) const; // Mock functions diff --git a/unity-shared/UpstartWrapper.cpp b/unity-shared/UpstartWrapper.cpp index b5986b918..284668b41 100644 --- a/unity-shared/UpstartWrapper.cpp +++ b/unity-shared/UpstartWrapper.cpp @@ -53,7 +53,7 @@ void UpstartWrapper::Impl::Emit(std::string const& name) DBUS_PATH_UPSTART, DBUS_INTERFACE_UPSTART, G_BUS_TYPE_SESSION, flags); - proxy->Call("EmitEvent", g_variant_new("(sasb)", name.c_str(), nullptr, 0), [proxy] (GVariant*) {}); + proxy->CallBegin("EmitEvent", g_variant_new("(sasb)", name.c_str(), nullptr, 0), [proxy] (GVariant*, glib::Error const&) {}); } // diff --git a/unity-shared/WindowButtons.cpp b/unity-shared/WindowButtons.cpp index af2bf512e..bd88aaf1d 100644 --- a/unity-shared/WindowButtons.cpp +++ b/unity-shared/WindowButtons.cpp @@ -420,10 +420,23 @@ void WindowButtons::OnRestoreClicked(nux::Button *button) { WindowManager& wm = WindowManager::Default(); Window to_restore = controlled_window(); + int button = nux::GetGraphicsDisplay()->GetCurrentEvent().GetEventButton(); wm.Raise(to_restore); wm.Activate(to_restore); - wm.Restore(to_restore); + + if (button == nux::NUX_MOUSE_BUTTON1) + { + wm.Restore(to_restore); + } + else if (button == nux::NUX_MOUSE_BUTTON2) + { + wm.VerticallyMaximize(to_restore); + } + else if (button == nux::NUX_MOUSE_BUTTON3) + { + wm.HorizontallyMaximize(to_restore); + } } restore_clicked.emit(); diff --git a/unity-shared/WindowManager.h b/unity-shared/WindowManager.h index 3502941d0..cb38aea77 100644 --- a/unity-shared/WindowManager.h +++ b/unity-shared/WindowManager.h @@ -170,6 +170,7 @@ public: virtual bool IsOnscreenKeyboard(Window window_id) const = 0; virtual std::string GetStringProperty(Window, Atom) const = 0; + virtual void SetCardinalProperty(Window, Atom, std::vector<long> const&) = 0; virtual std::vector<long> GetCardinalProperty(Window, Atom) const = 0; virtual Cursor GetCachedCursor(unsigned int cursor_name) const = 0; diff --git a/unity-shared/XWindowManager.cpp b/unity-shared/XWindowManager.cpp index 71b20ffd6..d6222f8b8 100644 --- a/unity-shared/XWindowManager.cpp +++ b/unity-shared/XWindowManager.cpp @@ -123,6 +123,12 @@ std::string XWindowManager::GetStringProperty(Window window_id, Atom atom) const return std::string(val, n_items); } +void XWindowManager::SetCardinalProperty(Window window_id, Atom atom, std::vector<long> const& values) +{ + XChangeProperty(screen->dpy(), window_id, atom, XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) values.data(), values.size()); +} + std::vector<long> XWindowManager::GetCardinalProperty(Window window_id, Atom atom) const { Atom type; diff --git a/unity-shared/XWindowManager.h b/unity-shared/XWindowManager.h index 211d92029..f6c4bbd04 100644 --- a/unity-shared/XWindowManager.h +++ b/unity-shared/XWindowManager.h @@ -36,6 +36,7 @@ public: std::string GetWindowName(Window window_id) const; bool IsOnscreenKeyboard(Window window_id) const; std::string GetStringProperty(Window window_id, Atom atom) const; + void SetCardinalProperty(Window, Atom, std::vector<long> const&); std::vector<long> GetCardinalProperty(Window, Atom) const; }; |
