Merge lp:~3v1n0/unity/input-monitor-menus-scrubbing-triangolation into lp:unity

Proposed by Marco Trevisan (Treviño) on 2016-08-30
Status: Superseded
Proposed branch: lp:~3v1n0/unity/input-monitor-menus-scrubbing-triangolation
Merge into: lp:unity
Diff against target: 2205 lines (+870/-400)
38 files modified
decorations/DecoratedWindow.cpp (+2/-9)
decorations/DecorationsMenuLayout.cpp (+35/-37)
decorations/DecorationsMenuLayout.h (+3/-2)
decorations/DecorationsPriv.h (+0/-1)
launcher/EdgeBarrierController.cpp (+32/-115)
launcher/EdgeBarrierControllerPrivate.h (+1/-6)
lockscreen/KylinLockScreenShield.cpp (+1/-1)
lockscreen/LockScreenBaseShield.cpp (+0/-2)
lockscreen/LockScreenBaseShield.h (+3/-4)
lockscreen/LockScreenController.cpp (+3/-3)
lockscreen/LockScreenController.h (+1/-2)
lockscreen/LockScreenPanel.cpp (+25/-56)
lockscreen/LockScreenPanel.h (+5/-9)
lockscreen/LockScreenShield.cpp (+7/-6)
lockscreen/LockScreenShield.h (+4/-3)
lockscreen/LockScreenShieldFactory.cpp (+2/-2)
lockscreen/LockScreenShieldFactory.h (+3/-3)
panel/PanelIndicatorEntryView.cpp (+10/-0)
panel/PanelMenuView.cpp (+1/-7)
panel/PanelMenuView.h (+0/-2)
panel/PanelView.cpp (+3/-115)
panel/PanelView.h (+3/-12)
plugins/unityshell/src/unityshell.h (+2/-0)
tests/test_edge_barrier_controller.cpp (+2/-0)
tests/test_lockscreen_controller.cpp (+2/-2)
tests/test_panel_controller.cpp (+2/-0)
tests/test_panel_view.cpp (+2/-0)
unity-shared/CMakeLists.txt (+1/-0)
unity-shared/InputMonitor.cpp (+408/-0)
unity-shared/InputMonitor.h (+67/-0)
unity-shared/MenuManager.cpp (+160/-1)
unity-shared/MenuManager.h (+4/-0)
unity-shared/SigcSlotHash.h (+64/-0)
unity-shared/StandaloneWindowManager.cpp (+3/-0)
unity-shared/StandaloneWindowManager.h (+1/-0)
unity-shared/WindowManager.h (+1/-0)
unity-shared/XWindowManager.cpp (+6/-0)
unity-shared/XWindowManager.h (+1/-0)
To merge this branch: bzr merge lp:~3v1n0/unity/input-monitor-menus-scrubbing-triangolation
Reviewer Review Type Date Requested Status
Unity Team 2016-08-30 Pending
Review via email: mp+304352@code.launchpad.net

This proposal has been superseded by a proposal from 2016-08-30.

Commit message

MenuManager: add support for mouse trackers with triangle algorithm support

It allows to register pointer trackers with menu entries selection by using the triangle
technique which prevents menus items from being opened on quick menu scrubbing

Then use use menu::Manager pointer tracker for entries activation in PanelView, LockScreenPanel and DecorationsMenuLayout

To post a comment you must log in.
3827. By Marco Trevisan (Treviño) on 2016-08-30

MenuManager: update active tracker on removal

3828. By Marco Trevisan (Treviño) on 2016-08-30

SigcSlotHash: ensure it works with GCC-6

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'decorations/DecoratedWindow.cpp'
2--- decorations/DecoratedWindow.cpp 2016-08-06 16:24:45 +0000
3+++ decorations/DecoratedWindow.cpp 2016-08-30 10:27:02 +0000
4@@ -37,7 +37,6 @@
5 {
6 namespace
7 {
8-const std::string MENUS_PANEL_NAME = "WindowLIM";
9 const int SHADOW_BLUR_MARGIN_FACTOR = 2;
10 }
11
12@@ -55,7 +54,6 @@
13 , deco_elements_(cu::DecorationElement::NONE)
14 , last_mwm_decor_(win_->mwmDecor())
15 , last_actions_(win_->actions())
16- , panel_id_(MENUS_PANEL_NAME + std::to_string(win_->id()))
17 , cv_(Settings::Instance().em())
18 {
19 active.changed.connect([this] (bool active) {
20@@ -932,18 +930,13 @@
21 sliding_layout->mouse_owner = grab_edge_->mouse_owner();
22 }
23
24-inline std::string const& Window::Impl::GetMenusPanelID() const
25-{
26- return panel_id_;
27-}
28-
29 void Window::Impl::UnsetAppMenu()
30 {
31 if (!menus_)
32 return;
33
34 auto const& indicators = manager_->impl_->menu_manager_->Indicators();
35- indicators->SyncGeometries(GetMenusPanelID(), indicator::EntryLocationMap());
36+ indicators->SyncGeometries(menus_->MenubarId(), indicator::EntryLocationMap());
37 sliding_layout_->SetInputItem(nullptr);
38 grab_mouse_changed_->disconnect();
39 }
40@@ -956,7 +949,7 @@
41 auto const& indicators = manager_->impl_->menu_manager_->Indicators();
42 indicator::EntryLocationMap map;
43 menus_->ChildrenGeometries(map);
44- indicators->SyncGeometries(GetMenusPanelID(), map);
45+ indicators->SyncGeometries(menus_->MenubarId(), map);
46 }
47
48 bool Window::Impl::ActivateMenu(std::string const& entry_id)
49
50=== modified file 'decorations/DecorationsMenuLayout.cpp'
51--- decorations/DecorationsMenuLayout.cpp 2015-02-03 10:04:17 +0000
52+++ decorations/DecorationsMenuLayout.cpp 2016-08-30 10:27:02 +0000
53@@ -25,6 +25,10 @@
54 {
55 namespace decoration
56 {
57+namespace
58+{
59+const std::string MENUS_PANEL_NAME = "WindowLIM";
60+}
61
62 using namespace indicator;
63
64@@ -33,8 +37,8 @@
65 , show_now(false)
66 , menu_manager_(menu)
67 , win_(win)
68- , last_pointer_(-1, -1)
69 , dropdown_(std::make_shared<MenuDropdown>(menu_manager_->Indicators(), win))
70+ , menubar_id_(MENUS_PANEL_NAME + std::to_string(win_->id()))
71 {
72 visible = false;
73 }
74@@ -91,6 +95,11 @@
75 Relayout();
76 }
77
78+std::string const& MenuLayout::MenubarId() const
79+{
80+ return menubar_id_;
81+}
82+
83 bool MenuLayout::ActivateMenu(std::string const& entry_id)
84 {
85 MenuEntry::Ptr target;
86@@ -117,16 +126,29 @@
87 if (!activated)
88 activated = dropdown_->ActivateChild(target);
89
90- if (activated)
91- {
92- // Since this generally happens on keyboard activation we need to avoid that
93- // the mouse position would interfere with this
94- last_pointer_.set(pointerX, pointerY);
95- }
96-
97 return activated;
98 }
99
100+bool MenuLayout::ActivateMenu(CompPoint const& pos)
101+{
102+ if (!Geometry().contains(pos))
103+ return false;
104+
105+ for (auto const& item : items_)
106+ {
107+ if (!item->visible() || !item->sensitive())
108+ continue;
109+
110+ if (item->Geometry().contains(pos))
111+ {
112+ std::static_pointer_cast<MenuEntry>(item)->ShowMenu(1);
113+ return true;
114+ }
115+ }
116+
117+ return false;
118+}
119+
120 void MenuLayout::OnEntryMouseOwnershipChanged(bool owner)
121 {
122 mouse_owner = owner;
123@@ -154,39 +176,15 @@
124 {
125 active = actived;
126
127- if (active && !pointer_tracker_ && items_.size() > 1)
128+ if (active && items_.size() > 1)
129 {
130- pointer_tracker_.reset(new glib::Timeout(16));
131- pointer_tracker_->Run([this] {
132- Window win;
133- int i, x, y;
134- unsigned int ui;
135-
136- XQueryPointer(screen->dpy(), screen->root(), &win, &win, &x, &y, &i, &i, &ui);
137-
138- if (last_pointer_.x() != x || last_pointer_.y() != y)
139- {
140- last_pointer_.set(x, y);
141-
142- for (auto const& item : items_)
143- {
144- if (!item->visible() || !item->sensitive())
145- continue;
146-
147- if (item->Geometry().contains(last_pointer_))
148- {
149- std::static_pointer_cast<MenuEntry>(item)->ShowMenu(1);
150- break;
151- }
152- }
153- }
154-
155- return true;
156- });
157+ menu_manager_->RegisterTracker(menubar_id_, (sigc::track_obj([this] (int x, int y, double speed) {
158+ ActivateMenu(CompPoint(x, y));
159+ }, *this)));
160 }
161 else if (!active)
162 {
163- pointer_tracker_.reset();
164+ menu_manager_->UnregisterTracker(menubar_id_);
165 }
166 }
167
168
169=== modified file 'decorations/DecorationsMenuLayout.h'
170--- decorations/DecorationsMenuLayout.h 2014-02-13 03:01:30 +0000
171+++ decorations/DecorationsMenuLayout.h 2016-08-30 10:27:02 +0000
172@@ -42,7 +42,9 @@
173
174 void Setup();
175 bool ActivateMenu(std::string const& entry_id);
176+ bool ActivateMenu(CompPoint const&);
177 void ChildrenGeometries(indicator::EntryLocationMap&) const;
178+ std::string const& MenubarId() const;
179
180 protected:
181 void DoRelayout() override;
182@@ -55,10 +57,9 @@
183
184 menu::Manager::Ptr menu_manager_;
185 CompWindow* win_;
186- CompPoint last_pointer_;
187- glib::Source::UniquePtr pointer_tracker_;
188 glib::Source::UniquePtr show_now_timeout_;
189 std::shared_ptr<MenuDropdown> dropdown_;
190+ std::string menubar_id_;
191 };
192
193 } // decoration namespace
194
195=== modified file 'decorations/DecorationsPriv.h'
196--- decorations/DecorationsPriv.h 2016-08-06 16:24:45 +0000
197+++ decorations/DecorationsPriv.h 2016-08-30 10:27:02 +0000
198@@ -162,7 +162,6 @@
199 connection::Wrapper dpi_changed_;
200 connection::Wrapper grab_mouse_changed_;
201 std::string last_title_;
202- std::string panel_id_;
203 std::vector<cu::SimpleTextureQuad> bg_textures_;
204 cu::PixmapTexture::Ptr shaped_shadow_pixmap_;
205 std::shared_ptr<ForceQuitDialog> force_quit_;
206
207=== modified file 'launcher/EdgeBarrierController.cpp'
208--- launcher/EdgeBarrierController.cpp 2015-12-23 09:29:24 +0000
209+++ launcher/EdgeBarrierController.cpp 2016-08-30 10:27:02 +0000
210@@ -25,6 +25,7 @@
211 #include <NuxCore/Logger.h>
212 #include "unity-shared/UnitySettings.h"
213 #include "unity-shared/UScreen.h"
214+#include "unity-shared/InputMonitor.h"
215 #include "UnityCore/GLibSource.h"
216
217 namespace unity
218@@ -36,50 +37,12 @@
219 {
220 int const Y_BREAK_BUFFER = 20;
221 int const X_BREAK_BUFFER = 20;
222- int const MAJOR = 2;
223- int const MINOR = 3;
224 }
225
226 DECLARE_LOGGER(logger, "unity.edge_barrier_controller");
227
228-int GetXI2OpCode()
229-{
230- Display *dpy = nux::GetGraphicsDisplay()->GetX11Display();
231-
232- int opcode, event_base, error_base;
233- if (!XQueryExtension(dpy, "XFIXES",
234- &opcode,
235- &event_base,
236- &error_base))
237- {
238- LOG_ERROR(logger) << "Missing XFixes";
239- return -1;
240- }
241-
242- if (!XQueryExtension (dpy, "XInputExtension",
243- &opcode,
244- &event_base,
245- &error_base))
246- {
247- LOG_ERROR(logger) << "Missing XInput";
248- return -1;
249- }
250-
251- int maj = MAJOR;
252- int min = MINOR;
253-
254- if (XIQueryVersion(dpy, &maj, &min) == BadRequest)
255- {
256- LOG_ERROR(logger) << "Need XInput version 2.3";
257- return -1;
258- }
259-
260- return opcode;
261-}
262-
263 EdgeBarrierController::Impl::Impl(EdgeBarrierController *parent)
264- : xi2_opcode_(-1)
265- , edge_overcome_pressure_(0)
266+ : edge_overcome_pressure_(0)
267 , parent_(parent)
268 {
269 UScreen *uscreen = UScreen::GetDefault();
270@@ -119,8 +82,6 @@
271 options->option_changed.connect(sigc::mem_fun(this, &EdgeBarrierController::Impl::OnOptionsChanged));
272 SetupBarriers(UScreen::GetDefault()->GetMonitors());
273 });
274-
275- xi2_opcode_ = GetXI2OpCode();
276 }
277
278 EdgeBarrierController::Impl::~Impl()
279@@ -202,36 +163,30 @@
280 }
281 }
282
283-void SetupXI2Events()
284-{
285- Display *dpy = nux::GetGraphicsDisplay()->GetX11Display();
286-
287- unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 };
288- XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits };
289-
290- XISetMask(mask.mask, XI_BarrierHit);
291- XISetMask(mask.mask, XI_BarrierLeave);
292- XISelectEvents (dpy, DefaultRootWindow(dpy), &mask, 1);
293-}
294-
295 void EdgeBarrierController::Impl::SetupBarriers(std::vector<nux::Geometry> const& layout)
296 {
297 if (parent_->force_disable())
298 return;
299
300+ size_t monitors_size = layout.size();
301+ auto launcher_position = Settings::Instance().launcher_position();
302 bool edge_resist = parent_->sticky_edges();
303- auto launcher_position = Settings::Instance().launcher_position();
304-
305- for (unsigned i = 0; i < layout.size(); i++)
306+ bool needs_barrier = edge_resist && monitors_size > 1;
307+ bool needs_vertical_barrier = needs_barrier;
308+
309+ if (parent_->options()->hide_mode() != launcher::LauncherHideMode::LAUNCHER_HIDE_NEVER)
310+ needs_vertical_barrier = true;
311+
312+ for (unsigned i = 0; i < layout.size(); ++i)
313 {
314- auto vertical_barrier = vertical_barriers_[i];
315- auto horizontal_barrier = horizontal_barriers_[i];
316- auto monitor = layout[i];
317+ auto const& vertical_barrier = vertical_barriers_[i];
318+ auto const& horizontal_barrier = horizontal_barriers_[i];
319+ auto const& monitor = layout[i];
320
321 vertical_barrier->DestroyBarrier();
322 horizontal_barrier->DestroyBarrier();
323
324- if (edge_resist)
325+ if (needs_barrier)
326 {
327 horizontal_barrier->x1 = monitor.x;
328 horizontal_barrier->x2 = monitor.x + monitor.width;
329@@ -246,7 +201,7 @@
330 horizontal_barrier->ConstructBarrier();
331 }
332
333- if (!edge_resist && parent_->options()->hide_mode() == launcher::LauncherHideMode::LAUNCHER_HIDE_NEVER)
334+ if (!needs_vertical_barrier)
335 continue;
336
337 if (launcher_position == LauncherPosition::LEFT)
338@@ -273,8 +228,10 @@
339 vertical_barrier->ConstructBarrier();
340 }
341
342- SetupXI2Events();
343- AddEventFilter();
344+ if (needs_barrier || needs_vertical_barrier)
345+ input::Monitor::Get().RegisterClient(input::Events::BARRIER, sigc::mem_fun(this, &Impl::HandleEvent));
346+ else
347+ input::Monitor::Get().UnregisterClient(sigc::mem_fun(this, &Impl::HandleEvent));
348
349 float decay_responsiveness_mult = ((parent_->options()->edge_responsiveness() - 1) * .3f) + 1;
350 decaymulator_.rate_of_decay = parent_->options()->edge_decay_rate() * decay_responsiveness_mult;
351@@ -283,65 +240,25 @@
352 edge_overcome_pressure_ = parent_->options()->edge_overcome_pressure() * overcome_responsiveness_mult;
353 }
354
355-void EdgeBarrierController::Impl::AddEventFilter()
356-{
357- // Remove an old one, if it exists
358- nux::GetGraphicsDisplay()->RemoveEventFilter(this);
359-
360- nux::GraphicsDisplay::EventFilterArg event_filter;
361- event_filter.filter = &HandleEventCB;
362- event_filter.data = this;
363-
364- nux::GetGraphicsDisplay()->AddEventFilter(event_filter);
365-}
366-
367-bool EdgeBarrierController::Impl::HandleEvent(XEvent xevent)
368-{
369- Display *dpy = nux::GetGraphicsDisplay()->GetX11Display();
370- XGenericEventCookie *cookie = &xevent.xcookie;
371- bool ret = false;
372-
373- switch (cookie->evtype)
374- {
375- case (XI_BarrierHit):
376- {
377- if (XGetEventData(dpy, cookie))
378- {
379- XIBarrierEvent* barrier_event = (XIBarrierEvent*)cookie->data;
380- PointerBarrierWrapper::Ptr wrapper = FindBarrierEventOwner(barrier_event);
381-
382- if (wrapper)
383- ret = wrapper->HandleBarrierEvent(barrier_event);
384- }
385-
386- XFreeEventData(dpy, cookie);
387- break;
388- }
389- default:
390- break;
391- }
392-
393- return ret;
394-}
395-
396-bool EdgeBarrierController::Impl::HandleEventCB(XEvent xevent, void* data)
397-{
398- auto edge_barrier_controller = static_cast<EdgeBarrierController::Impl*>(data);
399- int const xi2_opcode = edge_barrier_controller->xi2_opcode_;
400-
401- if (xevent.type != GenericEvent || xevent.xcookie.extension != xi2_opcode)
402- return false;
403-
404- return edge_barrier_controller->HandleEvent(xevent);
405+void EdgeBarrierController::Impl::HandleEvent(XEvent const& xevent)
406+{
407+ if (xevent.xcookie.evtype != XI_BarrierHit)
408+ return;
409+
410+ auto* barrier_event = reinterpret_cast<XIBarrierEvent*>(xevent.xcookie.data);
411+ PointerBarrierWrapper::Ptr const& wrapper = FindBarrierEventOwner(barrier_event);
412+
413+ if (wrapper)
414+ wrapper->HandleBarrierEvent(barrier_event);
415 }
416
417 PointerBarrierWrapper::Ptr EdgeBarrierController::Impl::FindBarrierEventOwner(XIBarrierEvent* barrier_event)
418 {
419- for (auto barrier : vertical_barriers_)
420+ for (auto const& barrier : vertical_barriers_)
421 if (barrier->OwnsBarrierEvent(barrier_event->barrier))
422 return barrier;
423
424- for (auto barrier : horizontal_barriers_)
425+ for (auto const& barrier : horizontal_barriers_)
426 if (barrier->OwnsBarrierEvent(barrier_event->barrier))
427 return barrier;
428
429
430=== modified file 'launcher/EdgeBarrierControllerPrivate.h'
431--- launcher/EdgeBarrierControllerPrivate.h 2015-12-23 09:29:24 +0000
432+++ launcher/EdgeBarrierControllerPrivate.h 2016-08-30 10:27:02 +0000
433@@ -53,12 +53,8 @@
434 bool EventIsInsideYBreakZone(BarrierEvent::Ptr const& event);
435 bool EventIsInsideXBreakZone(BarrierEvent::Ptr const& event);
436
437- void AddEventFilter();
438-
439 PointerBarrierWrapper::Ptr FindBarrierEventOwner(XIBarrierEvent* barrier_event);
440-
441- static bool HandleEventCB(XEvent event, void* data);
442- bool HandleEvent(XEvent event);
443+ void HandleEvent(XEvent const&);
444
445 std::vector<PointerBarrierWrapper::Ptr> vertical_barriers_;
446 std::vector<PointerBarrierWrapper::Ptr> horizontal_barriers_;
447@@ -68,7 +64,6 @@
448
449 Decaymulator decaymulator_;
450 glib::Source::UniquePtr release_timeout_;
451- int xi2_opcode_;
452 float edge_overcome_pressure_;
453 EdgeBarrierController* parent_;
454 };
455
456=== modified file 'lockscreen/KylinLockScreenShield.cpp'
457--- lockscreen/KylinLockScreenShield.cpp 2015-12-07 03:09:28 +0000
458+++ lockscreen/KylinLockScreenShield.cpp 2016-08-30 10:27:02 +0000
459@@ -37,7 +37,7 @@
460 Accelerators::Ptr const& accelerators,
461 nux::ObjectPtr<AbstractUserPromptView> const& prompt_view,
462 int monitor_num, bool is_primary)
463- : BaseShield(session_manager, nullptr, accelerators, prompt_view, monitor_num, is_primary)
464+ : BaseShield(session_manager, accelerators, prompt_view, monitor_num, is_primary)
465 {
466 is_primary ? ShowPrimaryView() : ShowSecondaryView();
467 EnableInputWindow(true);
468
469=== modified file 'lockscreen/LockScreenBaseShield.cpp'
470--- lockscreen/LockScreenBaseShield.cpp 2015-12-03 14:13:10 +0000
471+++ lockscreen/LockScreenBaseShield.cpp 2016-08-30 10:27:02 +0000
472@@ -38,7 +38,6 @@
473 }
474
475 BaseShield::BaseShield(session::Manager::Ptr const& session,
476- indicator::Indicators::Ptr const& indicators,
477 Accelerators::Ptr const& accelerators,
478 nux::ObjectPtr<AbstractUserPromptView> const& prompt_view,
479 int monitor_num, bool is_primary)
480@@ -47,7 +46,6 @@
481 , monitor(monitor_num)
482 , scale(1.0)
483 , session_manager_(session)
484- , indicators_(indicators)
485 , accelerators_(accelerators)
486 , prompt_view_(prompt_view)
487 , bg_settings_(std::make_shared<BackgroundSettings>())
488
489=== modified file 'lockscreen/LockScreenBaseShield.h'
490--- lockscreen/LockScreenBaseShield.h 2016-03-31 09:51:33 +0000
491+++ lockscreen/LockScreenBaseShield.h 2016-08-30 10:27:02 +0000
492@@ -21,8 +21,8 @@
493 #define UNITY_LOCKSCREEN_BASE_SHIELD_H
494
495 #include <NuxCore/Property.h>
496+#include "UnityCore/ConnectionManager.h"
497 #include "UnityCore/SessionManager.h"
498-#include "UnityCore/Indicators.h"
499 #include "UnityCore/GLibSource.h"
500 #include "unity-shared/MockableBaseWindow.h"
501
502@@ -39,8 +39,8 @@
503 class BaseShield : public MockableBaseWindow
504 {
505 public:
506- BaseShield(session::Manager::Ptr const&, indicator::Indicators::Ptr const&,
507- Accelerators::Ptr const&, nux::ObjectPtr<AbstractUserPromptView> const&,
508+ BaseShield(session::Manager::Ptr const&, Accelerators::Ptr const&,
509+ nux::ObjectPtr<AbstractUserPromptView> const&,
510 int monitor_num, bool is_primary);
511
512 nux::Property<bool> primary;
513@@ -69,7 +69,6 @@
514 void UpdateScale();
515
516 session::Manager::Ptr session_manager_;
517- indicator::Indicators::Ptr indicators_;
518 Accelerators::Ptr accelerators_;
519 nux::ObjectPtr<AbstractUserPromptView> prompt_view_;
520 std::shared_ptr<BackgroundSettings> bg_settings_;
521
522=== modified file 'lockscreen/LockScreenController.cpp'
523--- lockscreen/LockScreenController.cpp 2016-07-04 12:45:06 +0000
524+++ lockscreen/LockScreenController.cpp 2016-08-30 10:27:02 +0000
525@@ -130,7 +130,7 @@
526
527 upstart_wrapper_->Emit("desktop-unlock");
528 accelerator_controller_.reset();
529- indicators_.reset();
530+ menu_manager_.reset();
531 }
532 else if (!prompt_activation_)
533 {
534@@ -252,7 +252,7 @@
535
536 if (i >= shields_size)
537 {
538- shield = shield_factory_->CreateShield(session_manager_, indicators_, accelerator_controller_->GetAccelerators(), prompt_view, i, i == primary);
539+ shield = shield_factory_->CreateShield(session_manager_, menu_manager_, accelerator_controller_->GetAccelerators(), prompt_view, i, i == primary);
540 is_new = true;
541 }
542
543@@ -462,7 +462,7 @@
544
545 void Controller::LockScreen()
546 {
547- indicators_ = std::make_shared<indicator::LockScreenDBusIndicators>();
548+ menu_manager_ = std::make_shared<menu::Manager>(std::make_shared<indicator::LockScreenDBusIndicators>(), key_grabber_);
549 upstart_wrapper_->Emit("desktop-lock");
550
551 accelerator_controller_ = std::make_shared<AcceleratorController>(key_grabber_);
552
553=== modified file 'lockscreen/LockScreenController.h'
554--- lockscreen/LockScreenController.h 2016-06-21 01:28:26 +0000
555+++ lockscreen/LockScreenController.h 2016-08-30 10:27:02 +0000
556@@ -30,7 +30,6 @@
557 #include "SuspendInhibitorManager.h"
558 #include "ScreenSaverDBusManager.h"
559 #include "unity-shared/BackgroundEffectHelper.h"
560-#include "unity-shared/KeyGrabber.h"
561 #include "unity-shared/UpstartWrapper.h"
562
563 namespace unity
564@@ -85,8 +84,8 @@
565
566 DBusManager::Ptr dbus_manager_;
567 session::Manager::Ptr session_manager_;
568+ menu::Manager::Ptr menu_manager_;
569 key::Grabber::Ptr key_grabber_;
570- indicator::Indicators::Ptr indicators_;
571 AcceleratorController::Ptr accelerator_controller_;
572 UpstartWrapper::Ptr upstart_wrapper_;
573 ShieldFactoryInterface::Ptr shield_factory_;
574
575=== modified file 'lockscreen/LockScreenPanel.cpp'
576--- lockscreen/LockScreenPanel.cpp 2015-11-05 14:55:54 +0000
577+++ lockscreen/LockScreenPanel.cpp 2016-08-30 10:27:02 +0000
578@@ -24,7 +24,7 @@
579
580 #include "LockScreenSettings.h"
581 #include "panel/PanelIndicatorsView.h"
582-#include "unity-shared/CairoTexture.h"
583+#include "unity-shared/InputMonitor.h"
584 #include "unity-shared/StaticCairoText.h"
585 #include "unity-shared/PanelStyle.h"
586 #include "unity-shared/RawPixel.h"
587@@ -38,24 +38,24 @@
588 namespace
589 {
590 const RawPixel PADDING = 5_em;
591+const nux::Color BG_COLOR(0.1, 0.1, 0.1, 0.4);
592 }
593
594 using namespace indicator;
595 using namespace panel;
596
597-Panel::Panel(int monitor_, Indicators::Ptr const& indicators, session::Manager::Ptr const& session_manager)
598+Panel::Panel(int monitor_, menu::Manager::Ptr const& menu_manager, session::Manager::Ptr const& session_manager)
599 : nux::View(NUX_TRACKER_LOCATION)
600 , active(false)
601 , monitor(monitor_)
602- , indicators_(indicators)
603+ , menu_manager_(menu_manager)
604 , needs_geo_sync_(true)
605 {
606 double scale = unity::Settings::Instance().em(monitor)->DPIScale();
607 auto* layout = new nux::HLayout();
608 layout->SetLeftAndRightPadding(PADDING.CP(scale), 0);
609 SetLayout(layout);
610-
611- BuildTexture();
612+ UpdateSize();
613
614 // Add setting
615 auto *hostname = new StaticCairoText(session_manager->HostName());
616@@ -72,34 +72,33 @@
617 indicators_view_->on_indicator_updated.connect(sigc::mem_fun(this, &Panel::OnIndicatorViewUpdated));
618 layout->AddView(indicators_view_, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL);
619
620- for (auto const& indicator : indicators_->GetIndicators())
621+ auto indicators = menu_manager_->Indicators();
622+ menu_manager_->RegisterTracker(GetPanelName(), (sigc::track_obj([this] (int x, int y, double speed) {
623+ indicators_view_->ActivateEntryAt(x, y);
624+ }, *this)));
625+
626+ for (auto const& indicator : indicators->GetIndicators())
627 AddIndicator(indicator);
628
629- indicators_->on_object_added.connect(sigc::mem_fun(this, &Panel::AddIndicator));
630- indicators_->on_object_removed.connect(sigc::mem_fun(this, &Panel::RemoveIndicator));
631- indicators_->on_entry_show_menu.connect(sigc::mem_fun(this, &Panel::OnEntryShowMenu));
632- indicators_->on_entry_activated.connect(sigc::mem_fun(this, &Panel::OnEntryActivated));
633- indicators_->on_entry_activate_request.connect(sigc::mem_fun(this, &Panel::OnEntryActivateRequest));
634+ indicators->on_object_added.connect(sigc::mem_fun(this, &Panel::AddIndicator));
635+ indicators->on_object_removed.connect(sigc::mem_fun(this, &Panel::RemoveIndicator));
636+ indicators->on_entry_show_menu.connect(sigc::mem_fun(this, &Panel::OnEntryShowMenu));
637+ indicators->on_entry_activated.connect(sigc::mem_fun(this, &Panel::OnEntryActivated));
638+ indicators->on_entry_activate_request.connect(sigc::mem_fun(this, &Panel::OnEntryActivateRequest));
639
640 monitor.changed.connect([this, hostname] (int monitor) {
641 double scale = unity::Settings::Instance().em(monitor)->DPIScale();
642 hostname->SetScale(scale);
643 static_cast<nux::HLayout*>(GetLayout())->SetLeftAndRightPadding(PADDING.CP(scale), 0);
644 indicators_view_->SetMonitor(monitor);
645- BuildTexture();
646+ UpdateSize();
647 QueueRelayout();
648 });
649 }
650
651-void Panel::BuildTexture()
652+void Panel::UpdateSize()
653 {
654 int height = panel::Style::Instance().PanelHeight(monitor);
655- nux::CairoGraphics context(CAIRO_FORMAT_ARGB32, 1, height);
656- auto* cr = context.GetInternalContext();
657- cairo_set_source_rgb(cr, 0.1, 0.1, 0.1);
658- cairo_paint_with_alpha(cr, 0.4);
659- bg_texture_ = texture_ptr_from_cairo_graphics(context);
660-
661 view_layout_->SetMinimumHeight(height);
662 view_layout_->SetMaximumHeight(height);
663 }
664@@ -165,12 +164,7 @@
665 if (!GetInputEventSensitivity())
666 return;
667
668- if (!active)
669- {
670- // This is ugly... But Nux fault!
671- WindowManager::Default().UnGrabMousePointer(CurrentTime, button, x, y);
672- active = true;
673- }
674+ active = true;
675 }
676
677 void Panel::OnEntryActivateRequest(std::string const& entry_id)
678@@ -184,36 +178,16 @@
679 if (!GetInputEventSensitivity() || (!panel.empty() && panel != GetPanelName()))
680 return;
681
682- bool active = !entry_id.empty();
683+ bool valid_entry = !entry_id.empty();
684
685- if (active && !WindowManager::Default().IsScreenGrabbed())
686+ if (valid_entry && !WindowManager::Default().IsScreenGrabbed())
687 {
688 // The menu didn't grab the keyboard, let's take it back.
689 nux::GetWindowCompositor().GrabKeyboardAdd(static_cast<nux::BaseWindow*>(GetTopLevelViewWindow()));
690 }
691
692- if (active && !track_menu_pointer_timeout_)
693- {
694- track_menu_pointer_timeout_.reset(new glib::Timeout(16));
695- track_menu_pointer_timeout_->Run([this] {
696- nux::Point const& mouse = nux::GetGraphicsDisplay()->GetMouseScreenCoord();
697- if (tracked_pointer_pos_ != mouse)
698- {
699- if (GetAbsoluteGeometry().IsPointInside(mouse.x, mouse.y))
700- indicators_view_->ActivateEntryAt(mouse.x, mouse.y);
701-
702- tracked_pointer_pos_ = mouse;
703- }
704-
705- return true;
706- });
707- }
708- else if (!active)
709- {
710- track_menu_pointer_timeout_.reset();
711- tracked_pointer_pos_ = {-1, -1};
712- this->active = false;
713- }
714+ if (!valid_entry)
715+ active = valid_entry;
716 }
717
718 void Panel::Draw(nux::GraphicsEngine& graphics_engine, bool force_draw)
719@@ -227,12 +201,7 @@
720 graphics_engine.PushClippingRectangle(geo);
721 nux::GetPainter().PaintBackground(graphics_engine, geo);
722
723- nux::TexCoordXForm texxform;
724- texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_CLAMP);
725- graphics_engine.QRP_1Tex(geo.x, geo.y, geo.width, geo.height,
726- bg_texture_->GetDeviceTexture(), texxform,
727- nux::color::White);
728-
729+ graphics_engine.QRP_Color(geo.x, geo.y, geo.width, geo.height, BG_COLOR);
730 view_layout_->ProcessDraw(graphics_engine, force_draw);
731
732 graphics_engine.PopClippingRectangle();
733@@ -242,7 +211,7 @@
734 {
735 EntryLocationMap locations;
736 indicators_view_->GetGeometryForSync(locations);
737- indicators_->SyncGeometries(GetPanelName(), locations);
738+ menu_manager_->Indicators()->SyncGeometries(GetPanelName(), locations);
739 needs_geo_sync_ = false;
740 }
741 }
742
743=== modified file 'lockscreen/LockScreenPanel.h'
744--- lockscreen/LockScreenPanel.h 2016-03-31 09:51:33 +0000
745+++ lockscreen/LockScreenPanel.h 2016-08-30 10:27:02 +0000
746@@ -22,9 +22,9 @@
747
748 #include <Nux/Nux.h>
749 #include <Nux/View.h>
750-#include "UnityCore/Indicators.h"
751 #include "UnityCore/GLibSource.h"
752 #include "UnityCore/SessionManager.h"
753+#include "unity-shared/MenuManager.h"
754
755 namespace unity
756 {
757@@ -39,7 +39,7 @@
758 class Panel : public nux::View
759 {
760 public:
761- Panel(int monitor, indicator::Indicators::Ptr const&, session::Manager::Ptr const&);
762+ Panel(int monitor, menu::Manager::Ptr const&, session::Manager::Ptr const&);
763
764 nux::Property<bool> active;
765 nux::Property<int> monitor;
766@@ -55,20 +55,16 @@
767 void AddIndicator(indicator::Indicator::Ptr const&);
768 void RemoveIndicator(indicator::Indicator::Ptr const&);
769 void OnIndicatorViewUpdated();
770- void OnEntryActivated(std::string const& panel, std::string const& entry_id, nux::Rect const& geo);
771+ void OnEntryActivated(std::string const& panel, std::string const& entry_id, nux::Rect const&);
772 void OnEntryShowMenu(std::string const& entry_id, unsigned xid, int x, int y, unsigned button);
773 void OnEntryActivateRequest(std::string const& entry_id);
774
775- void BuildTexture();
776+ void UpdateSize();
777 std::string GetPanelName() const;
778
779- indicator::Indicators::Ptr indicators_;
780+ menu::Manager::Ptr menu_manager_;
781 panel::PanelIndicatorsView* indicators_view_;
782- nux::ObjectPtr<nux::BaseTexture> bg_texture_;
783-
784 bool needs_geo_sync_;
785- nux::Point tracked_pointer_pos_;
786- glib::Source::UniquePtr track_menu_pointer_timeout_;
787 };
788
789 } // lockscreen namespace
790
791=== modified file 'lockscreen/LockScreenShield.cpp'
792--- lockscreen/LockScreenShield.cpp 2015-12-03 14:13:10 +0000
793+++ lockscreen/LockScreenShield.cpp 2016-08-30 10:27:02 +0000
794@@ -32,11 +32,12 @@
795 {
796
797 Shield::Shield(session::Manager::Ptr const& session_manager,
798- indicator::Indicators::Ptr const& indicators,
799+ menu::Manager::Ptr const& menu_manager,
800 Accelerators::Ptr const& accelerators,
801 nux::ObjectPtr<AbstractUserPromptView> const& prompt_view,
802 int monitor_num, bool is_primary)
803- : BaseShield(session_manager, indicators, accelerators, prompt_view, monitor_num, is_primary)
804+ : BaseShield(session_manager, accelerators, prompt_view, monitor_num, is_primary)
805+ , menu_manager_(menu_manager)
806 , panel_view_(nullptr)
807 {
808 is_primary ? ShowPrimaryView() : ShowSecondaryView();
809@@ -91,11 +92,11 @@
810
811 Panel* Shield::CreatePanel()
812 {
813- if (!indicators_ || !session_manager_)
814+ if (!menu_manager_ || !session_manager_)
815 return nullptr;
816
817- panel_view_ = new Panel(monitor, indicators_, session_manager_);
818- panel_active_conn_ = panel_view_->active.changed.connect([this] (bool active) {
819+ panel_view_ = new Panel(monitor, menu_manager_, session_manager_);
820+ panel_view_->active.changed.connect(sigc::track_obj([this] (bool active) {
821 if (primary())
822 {
823 if (active)
824@@ -109,7 +110,7 @@
825 GrabScreen(false);
826 }
827 }
828- });
829+ }, *this));
830
831 return panel_view_;
832 }
833
834=== modified file 'lockscreen/LockScreenShield.h'
835--- lockscreen/LockScreenShield.h 2015-12-03 14:13:10 +0000
836+++ lockscreen/LockScreenShield.h 2016-08-30 10:27:02 +0000
837@@ -20,8 +20,9 @@
838 #ifndef UNITY_LOCKSCREEN_SHIELD_H
839 #define UNITY_LOCKSCREEN_SHIELD_H
840
841-#include <UnityCore/ConnectionManager.h>
842 #include "LockScreenBaseShield.h"
843+#include "unity-shared/MenuManager.h"
844+
845
846 namespace unity
847 {
848@@ -35,7 +36,7 @@
849 {
850 public:
851 Shield(session::Manager::Ptr const&,
852- indicator::Indicators::Ptr const&,
853+ menu::Manager::Ptr const&,
854 Accelerators::Ptr const&,
855 nux::ObjectPtr<AbstractUserPromptView> const&,
856 int monitor, bool is_primary);
857@@ -50,7 +51,7 @@
858 void ShowPrimaryView() override;
859 Panel* CreatePanel();
860
861- connection::Wrapper panel_active_conn_;
862+ menu::Manager::Ptr menu_manager_;
863 Panel* panel_view_;
864 };
865
866
867=== modified file 'lockscreen/LockScreenShieldFactory.cpp'
868--- lockscreen/LockScreenShieldFactory.cpp 2015-12-04 08:17:46 +0000
869+++ lockscreen/LockScreenShieldFactory.cpp 2016-08-30 10:27:02 +0000
870@@ -28,7 +28,7 @@
871 {
872
873 nux::ObjectPtr<BaseShield> ShieldFactory::CreateShield(session::Manager::Ptr const& session_manager,
874- indicator::Indicators::Ptr const& indicators,
875+ menu::Manager::Ptr const& menu_manager,
876 Accelerators::Ptr const& accelerators,
877 nux::ObjectPtr<AbstractUserPromptView> const& prompt_view,
878 int monitor, bool is_primary)
879@@ -38,7 +38,7 @@
880 if (Settings::Instance().desktop_type() == DesktopType::UBUNTUKYLIN)
881 shield = new KylinShield(session_manager, accelerators, prompt_view, monitor, is_primary);
882 else
883- shield = new Shield(session_manager, indicators, accelerators, prompt_view, monitor, is_primary);
884+ shield = new Shield(session_manager, menu_manager, accelerators, prompt_view, monitor, is_primary);
885
886 return shield;
887 }
888
889=== modified file 'lockscreen/LockScreenShieldFactory.h'
890--- lockscreen/LockScreenShieldFactory.h 2016-03-31 09:51:33 +0000
891+++ lockscreen/LockScreenShieldFactory.h 2016-08-30 10:27:02 +0000
892@@ -22,7 +22,7 @@
893
894 #include <NuxCore/NuxCore.h>
895 #include "UnityCore/SessionManager.h"
896-#include "UnityCore/Indicators.h"
897+#include "unity-shared/MenuManager.h"
898 #include "LockScreenAccelerators.h"
899
900 namespace unity
901@@ -41,7 +41,7 @@
902 virtual ~ShieldFactoryInterface() = default;
903
904 virtual nux::ObjectPtr<BaseShield> CreateShield(session::Manager::Ptr const&,
905- indicator::Indicators::Ptr const&,
906+ menu::Manager::Ptr const&,
907 Accelerators::Ptr const&,
908 nux::ObjectPtr<AbstractUserPromptView> const&,
909 int monitor, bool is_primary) = 0;
910@@ -50,7 +50,7 @@
911 struct ShieldFactory : ShieldFactoryInterface
912 {
913 nux::ObjectPtr<BaseShield> CreateShield(session::Manager::Ptr const&,
914- indicator::Indicators::Ptr const&,
915+ menu::Manager::Ptr const&,
916 Accelerators::Ptr const&,
917 nux::ObjectPtr<AbstractUserPromptView> const&,
918 int monitor, bool is_primary) override;
919
920=== modified file 'panel/PanelIndicatorEntryView.cpp'
921--- panel/PanelIndicatorEntryView.cpp 2016-02-17 13:14:37 +0000
922+++ panel/PanelIndicatorEntryView.cpp 2016-08-30 10:27:02 +0000
923@@ -33,6 +33,8 @@
924 #include "unity-shared/RawPixel.h"
925 #include "unity-shared/WindowManager.h"
926 #include "unity-shared/ThemeSettings.h"
927+#include "unity-shared/UBusWrapper.h"
928+#include "unity-shared/UBusMessages.h"
929 #include "unity-shared/UnitySettings.h"
930
931 namespace unity
932@@ -117,6 +119,9 @@
933 }
934 else
935 {
936+ if (overlay_showing_)
937+ UBusManager::SendMessage(UBUS_OVERLAY_CLOSE_REQUEST);
938+
939 WindowManager& wm = WindowManager::Default();
940
941 if (wm.IsExpoActive())
942@@ -140,6 +145,11 @@
943 wm.TerminateScale();
944 }
945
946+ // This is ugly... But Nux fault!
947+ auto const& abs_geo = GetAbsoluteGeometry();
948+ guint64 timestamp = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp;
949+ WindowManager::Default().UnGrabMousePointer(timestamp, button, abs_geo.x, abs_geo.y);
950+
951 Activate(button);
952 }
953 }
954
955=== modified file 'panel/PanelMenuView.cpp'
956--- panel/PanelMenuView.cpp 2015-12-16 15:12:05 +0000
957+++ panel/PanelMenuView.cpp 2016-08-30 10:27:02 +0000
958@@ -102,7 +102,6 @@
959 , ignore_menu_visibility_(false)
960 , integrated_menus_(menu_manager_->integrated_menus())
961 , always_show_menus_(menu_manager_->always_show_menus())
962- , ignore_leave_events_(false)
963 , desktop_name_(get_current_desktop())
964 {
965 if (ApplicationWindowPtr const& win = ApplicationManager::Default().GetActiveWindow())
966@@ -1814,14 +1813,9 @@
967 }
968 }
969
970-void PanelMenuView::IgnoreLeaveEvents(bool ignore)
971-{
972- ignore_leave_events_ = ignore;
973-}
974-
975 void PanelMenuView::OnPanelViewMouseLeave(int x, int y, unsigned long mouse_button_state, unsigned long special_keys_state)
976 {
977- if (always_show_menus_ || ignore_leave_events_)
978+ if (always_show_menus_)
979 return;
980
981 if (is_inside_)
982
983=== modified file 'panel/PanelMenuView.h'
984--- panel/PanelMenuView.h 2015-11-05 14:54:13 +0000
985+++ panel/PanelMenuView.h 2016-08-30 10:27:02 +0000
986@@ -56,7 +56,6 @@
987 bool HasKeyActivableMenus() const;
988
989 void NotifyAllMenusClosed();
990- void IgnoreLeaveEvents(bool);
991
992 virtual void AddIndicator(indicator::Indicator::Ptr const& indicator);
993
994@@ -192,7 +191,6 @@
995 bool ignore_menu_visibility_;
996 bool integrated_menus_;
997 bool always_show_menus_;
998- bool ignore_leave_events_;
999
1000 nux::Geometry monitor_geo_;
1001 const std::string desktop_name_;
1002
1003=== modified file 'panel/PanelView.cpp'
1004--- panel/PanelView.cpp 2016-08-12 13:57:19 +0000
1005+++ panel/PanelView.cpp 2016-08-30 10:27:02 +0000
1006@@ -42,6 +42,7 @@
1007 namespace
1008 {
1009 const RawPixel TRIANGLE_THRESHOLD = 5_em;
1010+const double SCRUB_VELOCITY_THRESHOLD = 0.05;
1011 const int refine_gradient_midpoint = 959;
1012 }
1013
1014@@ -113,10 +114,9 @@
1015
1016 remote_->on_object_added.connect(sigc::mem_fun(this, &PanelView::OnObjectAdded));
1017 remote_->on_object_removed.connect(sigc::mem_fun(this, &PanelView::OnObjectRemoved));
1018- remote_->on_entry_activated.connect(sigc::mem_fun(this, &PanelView::OnEntryActivated));
1019- remote_->on_entry_show_menu.connect(sigc::mem_fun(this, &PanelView::OnEntryShowMenu));
1020 menus->key_activate_entry.connect(sigc::mem_fun(this, &PanelView::ActivateEntry));
1021 menus->open_first.connect(sigc::mem_fun(this, &PanelView::ActivateFirstSensitive));
1022+ menus->RegisterTracker(GetPanelName(), sigc::mem_fun(this, &PanelView::OnMenuPointerMoved));
1023
1024 ubus_manager_.RegisterInterest(UBUS_OVERLAY_HIDDEN, sigc::mem_fun(this, &PanelView::OnOverlayHidden));
1025 ubus_manager_.RegisterInterest(UBUS_OVERLAY_SHOWN, sigc::mem_fun(this, &PanelView::OnOverlayShown));
1026@@ -627,7 +627,7 @@
1027 QueueDraw();
1028 }
1029
1030-void PanelView::OnMenuPointerMoved(int x, int y)
1031+void PanelView::OnMenuPointerMoved(int x, int y, double speed)
1032 {
1033 nux::Geometry const& geo = GetAbsoluteGeometry();
1034
1035@@ -648,116 +648,6 @@
1036 }
1037 }
1038
1039-static bool PointInTriangle(nux::Point const& p, nux::Point const& t0, nux::Point const& t1, nux::Point const& t2)
1040-{
1041- int s = t0.y * t2.x - t0.x * t2.y + (t2.y - t0.y) * p.x + (t0.x - t2.x) * p.y;
1042- int t = t0.x * t1.y - t0.y * t1.x + (t0.y - t1.y) * p.x + (t1.x - t0.x) * p.y;
1043-
1044- if ((s < 0) != (t < 0))
1045- return false;
1046-
1047- int A = -t1.y * t2.x + t0.y * (t2.x - t1.x) + t0.x * (t1.y - t2.y) + t1.x * t2.y;
1048- if (A < 0)
1049- {
1050- s = -s;
1051- t = -t;
1052- A = -A;
1053- }
1054-
1055- return s > 0 && t > 0 && (s + t) < A;
1056-}
1057-
1058-static double GetMouseVelocity(nux::Point const& p0, nux::Point const& p1, util::Timer &timer)
1059-{
1060- int dx, dy;
1061- double speed;
1062- auto millis = timer.ElapsedMicroSeconds();
1063-
1064- if (millis == 0)
1065- return 1;
1066-
1067- dx = p0.x - p1.x;
1068- dy = p0.y - p1.y;
1069-
1070- speed = sqrt(dx * dx + dy * dy) / millis * 1000;
1071-
1072- return speed;
1073-}
1074-
1075-bool PanelView::TrackMenuPointer()
1076-{
1077- nux::Point const& mouse = nux::GetGraphicsDisplay()->GetMouseScreenCoord();
1078- double speed = GetMouseVelocity(mouse, tracked_pointer_pos_, mouse_tracker_timer_);
1079-
1080- mouse_tracker_timer_.Reset();
1081- tracked_pointer_pos_ = mouse;
1082-
1083- double scale = Settings::Instance().em(monitor_)->DPIScale();
1084- if (speed > 0 && PointInTriangle(mouse,
1085- nux::Point(triangle_top_corner_.x, std::max(triangle_top_corner_.y - TRIANGLE_THRESHOLD.CP(scale), 0)),
1086- nux::Point(menu_geo_.x, menu_geo_.y),
1087- nux::Point(menu_geo_.x + menu_geo_.width, menu_geo_.y)))
1088- {
1089- return true;
1090- }
1091-
1092- if (mouse != triangle_top_corner_)
1093- {
1094- triangle_top_corner_ = mouse;
1095- OnMenuPointerMoved(mouse.x, mouse.y);
1096- }
1097-
1098- return true;
1099-}
1100-
1101-void PanelView::OnEntryActivated(std::string const& panel, std::string const& entry_id, nux::Rect const& menu_geo)
1102-{
1103- if (!panel.empty() && panel != GetPanelName())
1104- return;
1105-
1106- menu_geo_ = menu_geo;
1107-
1108- bool active = !entry_id.empty();
1109- if (active && !track_menu_pointer_timeout_)
1110- {
1111- //
1112- // Track menus being scrubbed at 60Hz (about every 16 millisec)
1113- // It might sound ugly, but it's far nicer (and more responsive) than the
1114- // code it replaces which used to capture motion events in another process
1115- // (unity-panel-service) and send them to us over dbus.
1116- // NOTE: The reason why we have to use a timer instead of tracking motion
1117- // events is because the motion events will never be delivered to this
1118- // process. All the motion events will go to unity-panel-service while
1119- // scrubbing because the active panel menu has (needs) the pointer grab.
1120- //
1121- mouse_tracker_timer_.Reset();
1122- triangle_top_corner_ = nux::GetGraphicsDisplay()->GetMouseScreenCoord();
1123- track_menu_pointer_timeout_.reset(new glib::Timeout(16));
1124- track_menu_pointer_timeout_->Run(sigc::mem_fun(this, &PanelView::TrackMenuPointer));
1125- }
1126- else if (!active)
1127- {
1128- track_menu_pointer_timeout_.reset();
1129- menu_view_->NotifyAllMenusClosed();
1130- tracked_pointer_pos_ = {-1, -1};
1131- }
1132-
1133- if (overlay_is_open_)
1134- ubus_manager_.SendMessage(UBUS_OVERLAY_CLOSE_REQUEST);
1135-}
1136-
1137-void PanelView::OnEntryShowMenu(std::string const& entry_id, unsigned xid,
1138- int x, int y, unsigned button)
1139-{
1140- if (!track_menu_pointer_timeout_)
1141- {
1142- // This is ugly... But Nux fault!
1143- menu_view_->IgnoreLeaveEvents(true);
1144- WindowManager::Default().UnGrabMousePointer(CurrentTime, button, x, y);
1145- menu_view_->IgnoreLeaveEvents(false);
1146- }
1147-}
1148-
1149 bool PanelView::ActivateFirstSensitive()
1150 {
1151 if (!IsActive())
1152@@ -768,7 +658,6 @@
1153 {
1154 // Since this only happens on keyboard events, we need to prevent that the
1155 // pointer tracker would select another entry.
1156- tracked_pointer_pos_ = nux::GetGraphicsDisplay()->GetMouseScreenCoord();
1157 return true;
1158 }
1159
1160@@ -785,7 +674,6 @@
1161 {
1162 // Since this only happens on keyboard events, we need to prevent that the
1163 // pointer tracker would select another entry.
1164- tracked_pointer_pos_ = nux::GetGraphicsDisplay()->GetMouseScreenCoord();
1165 return true;
1166 }
1167
1168
1169=== modified file 'panel/PanelView.h'
1170--- panel/PanelView.h 2016-03-30 18:18:07 +0000
1171+++ panel/PanelView.h 2016-08-30 10:27:02 +0000
1172@@ -35,7 +35,6 @@
1173 #include "unity-shared/Introspectable.h"
1174 #include "unity-shared/MenuManager.h"
1175 #include "unity-shared/MockableBaseWindow.h"
1176-#include "unity-shared/Timer.h"
1177 #include "PanelMenuView.h"
1178 #include "PanelTray.h"
1179 #include "PanelIndicatorsView.h"
1180@@ -88,9 +87,6 @@
1181 void OnObjectAdded(indicator::Indicator::Ptr const& proxy);
1182 void OnObjectRemoved(indicator::Indicator::Ptr const& proxy);
1183 void OnIndicatorViewUpdated();
1184- void OnMenuPointerMoved(int x, int y);
1185- void OnEntryActivated(std::string const& panel, std::string const& entry_id, nux::Rect const& geo);
1186- void OnEntryShowMenu(std::string const& entry_id, unsigned xid, int x, int y, unsigned button);
1187
1188 private:
1189 std::string GetPanelName() const;
1190@@ -100,6 +96,8 @@
1191 void OnSpreadInitiate();
1192 void OnSpreadTerminate();
1193 void OnLowGfxChanged();
1194+ void OnMenuPointerMoved(int x, int y, double speed);
1195+ void OnActiveEntryEvent(XEvent const&);
1196 void EnableOverlayMode(bool);
1197 void LoadTextures();
1198
1199@@ -109,7 +107,6 @@
1200 bool IsTransparent();
1201 void UpdateBackground();
1202 void ForceUpdateBackground();
1203- bool TrackMenuPointer();
1204 void SyncGeometries();
1205 void AddPanelView(PanelIndicatorsView* child, unsigned int stretchFactor);
1206
1207@@ -133,10 +130,6 @@
1208 BaseTexturePtr bg_refine_single_column_tex_;
1209 std::unique_ptr<nux::AbstractPaintLayer> bg_refine_single_column_layer_;
1210
1211- std::string active_overlay_;
1212- nux::Point tracked_pointer_pos_, triangle_top_corner_;
1213- util::Timer mouse_tracker_timer_;
1214-
1215 bool is_dirty_;
1216 bool opacity_maximized_toggle_;
1217 bool needs_geo_sync_;
1218@@ -144,15 +137,13 @@
1219 float opacity_;
1220 int monitor_;
1221 int stored_dash_width_;
1222-
1223- nux::Geometry menu_geo_;
1224+ std::string active_overlay_;
1225
1226 connection::Manager on_indicator_updated_connections_;
1227 connection::Manager maximized_opacity_toggle_connections_;
1228 BackgroundEffectHelper bg_effect_helper_;
1229 nux::ObjectPtr<nux::IOpenGLBaseTexture> bg_blur_texture_;
1230 UBusManager ubus_manager_;
1231- glib::Source::UniquePtr track_menu_pointer_timeout_;
1232 };
1233
1234 } // namespace panel
1235
1236=== modified file 'plugins/unityshell/src/unityshell.h'
1237--- plugins/unityshell/src/unityshell.h 2016-08-12 11:21:48 +0000
1238+++ plugins/unityshell/src/unityshell.h 2016-08-30 10:27:02 +0000
1239@@ -56,6 +56,7 @@
1240 #include "DashStyle.h"
1241 #include "EdgeBarrierController.h"
1242 #include "FavoriteStoreGSettings.h"
1243+#include "InputMonitor.h"
1244 #include "ShortcutController.h"
1245 #include "LauncherController.h"
1246 #include "LockScreenController.h"
1247@@ -317,6 +318,7 @@
1248 internal::FavoriteStoreGSettings favorite_store_;
1249 ThumbnailGenerator thumbnail_generator_;
1250 lockscreen::Settings lockscreen_settings_;
1251+ input::Monitor input_monitor_;
1252
1253 /* The window thread should be the last thing removed, as c++ does it in reverse order */
1254 std::unique_ptr<nux::WindowThread> wt;
1255
1256=== modified file 'tests/test_edge_barrier_controller.cpp'
1257--- tests/test_edge_barrier_controller.cpp 2015-01-15 15:02:24 +0000
1258+++ tests/test_edge_barrier_controller.cpp 2016-08-30 10:27:02 +0000
1259@@ -25,6 +25,7 @@
1260
1261 #include "EdgeBarrierController.h"
1262 #include "EdgeBarrierControllerPrivate.h"
1263+#include "InputMonitor.h"
1264
1265 using namespace unity;
1266 using namespace unity::ui;
1267@@ -108,6 +109,7 @@
1268
1269 TestBarrierSubscriber horizontal_subscribers_[monitors::MAX];
1270 TestBarrierSubscriber vertical_subscribers_[monitors::MAX];
1271+ input::Monitor im;
1272 MockUScreen uscreen;
1273 EdgeBarrierController bc;
1274 };
1275
1276=== modified file 'tests/test_lockscreen_controller.cpp'
1277--- tests/test_lockscreen_controller.cpp 2016-06-21 14:40:26 +0000
1278+++ tests/test_lockscreen_controller.cpp 2016-08-30 10:27:02 +0000
1279@@ -55,7 +55,7 @@
1280 struct MockShield : BaseShield
1281 {
1282 MockShield()
1283- : BaseShield(nullptr, nullptr, nullptr, nux::ObjectPtr<AbstractUserPromptView>(), 0, false)
1284+ : BaseShield(nullptr, nullptr, nux::ObjectPtr<AbstractUserPromptView>(), 0, false)
1285 {}
1286
1287 MOCK_CONST_METHOD0(IsIndicatorOpen, bool());
1288@@ -67,7 +67,7 @@
1289 struct ShieldFactoryMock : ShieldFactoryInterface
1290 {
1291 nux::ObjectPtr<BaseShield> CreateShield(session::Manager::Ptr const&,
1292- indicator::Indicators::Ptr const&,
1293+ menu::Manager::Ptr const&,
1294 Accelerators::Ptr const&,
1295 nux::ObjectPtr<AbstractUserPromptView> const&,
1296 int, bool) override
1297
1298=== modified file 'tests/test_panel_controller.cpp'
1299--- tests/test_panel_controller.cpp 2014-03-21 04:40:12 +0000
1300+++ tests/test_panel_controller.cpp 2016-08-30 10:27:02 +0000
1301@@ -19,6 +19,7 @@
1302
1303 #include <gmock/gmock.h>
1304
1305+#include "InputMonitor.h"
1306 #include "PanelController.h"
1307 #include "PanelStyle.h"
1308 #include "PanelView.h"
1309@@ -46,6 +47,7 @@
1310 menu::MockManager::Ptr menus;
1311 ui::EdgeBarrierController::Ptr edge_barriers;
1312 launcher::Options::Ptr options;
1313+ input::Monitor im;
1314 };
1315
1316 TEST_F(TestPanelController, Construction)
1317
1318=== modified file 'tests/test_panel_view.cpp'
1319--- tests/test_panel_view.cpp 2014-12-12 22:33:24 +0000
1320+++ tests/test_panel_view.cpp 2016-08-30 10:27:02 +0000
1321@@ -25,6 +25,7 @@
1322 #include "unity-shared/PanelStyle.h"
1323 #include "unity-shared/UBusMessages.h"
1324 #include "unity-shared/UBusWrapper.h"
1325+ #include "InputMonitor.h"
1326
1327 #include "mock_menu_manager.h"
1328 #include "test_standalone_wm.h"
1329@@ -43,6 +44,7 @@
1330 nux::ObjectPtr<MockableBaseWindow> window_;
1331 nux::ObjectPtr<PanelView> panel_view_;
1332 testwrapper::StandaloneWM WM;
1333+ input::Monitor im;
1334
1335 TestPanelView()
1336 : window_(new MockableBaseWindow())
1337
1338=== modified file 'unity-shared/CMakeLists.txt'
1339--- unity-shared/CMakeLists.txt 2016-08-12 11:21:48 +0000
1340+++ unity-shared/CMakeLists.txt 2016-08-30 10:27:02 +0000
1341@@ -82,6 +82,7 @@
1342 set (UNITY_SHARED_SOURCES
1343 XKeyboardUtil.cpp
1344 XWindowManager.cpp
1345+ InputMonitor.cpp
1346 ${UNITY_SHARED_SOURCES}
1347 )
1348 else()
1349
1350=== added file 'unity-shared/InputMonitor.cpp'
1351--- unity-shared/InputMonitor.cpp 1970-01-01 00:00:00 +0000
1352+++ unity-shared/InputMonitor.cpp 2016-08-30 10:27:02 +0000
1353@@ -0,0 +1,408 @@
1354+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
1355+/*
1356+ * Copyright (C) 2014 Canonical Ltd
1357+ *
1358+ * This program is free software: you can redistribute it and/or modify
1359+ * it under the terms of the GNU General Public License version 3 as
1360+ * published by the Free Software Foundation.
1361+ *
1362+ * This program is distributed in the hope that it will be useful,
1363+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1364+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1365+ * GNU General Public License for more details.
1366+ *
1367+ * You should have received a copy of the GNU General Public License
1368+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1369+ *
1370+ * Authored by: Marco Trevisan <marco.trevisan@canonical.com>
1371+ */
1372+
1373+#include "InputMonitor.h"
1374+#include "SigcSlotHash.h"
1375+
1376+#include <Nux/Nux.h>
1377+#include <NuxCore/Logger.h>
1378+#include <X11/extensions/XInput2.h>
1379+#include <UnityCore/GLibSource.h>
1380+#include <unordered_set>
1381+#include <gdk/gdkx.h>
1382+#include <glib.h>
1383+
1384+namespace unity
1385+{
1386+namespace input
1387+{
1388+namespace
1389+{
1390+DECLARE_LOGGER(logger, "unity.input.monitor");
1391+
1392+Monitor* instance_ = nullptr;
1393+
1394+const unsigned XINPUT_MAJOR_VERSION = 2;
1395+const unsigned XINPUT_MINOR_VERSION = 3;
1396+
1397+bool operator&(Events l, Events r)
1398+{
1399+ typedef std::underlying_type<Events>::type ut;
1400+ return static_cast<ut>(static_cast<ut>(l) & static_cast<ut>(r));
1401+}
1402+
1403+Events& operator|=(Events& l, Events r)
1404+{
1405+ typedef std::underlying_type<Events>::type ut;
1406+ return l = static_cast<Events>(static_cast<ut>(l) | static_cast<ut>(r));
1407+}
1408+
1409+template <typename EVENT>
1410+void initialize_event_common(EVENT* ev, XIDeviceEvent* xiev)
1411+{
1412+ ev->serial = xiev->serial;
1413+ ev->send_event = xiev->send_event;
1414+ ev->display = xiev->display;
1415+ ev->window = xiev->event;
1416+ ev->root = xiev->root;
1417+ ev->subwindow = xiev->child;
1418+ ev->time = xiev->time;
1419+ ev->x = std::round(xiev->event_x);
1420+ ev->y = std::round(xiev->event_y);
1421+ ev->x_root = std::round(xiev->root_x);
1422+ ev->y_root = std::round(xiev->root_y);
1423+ ev->state = xiev->mods.effective;
1424+ ev->same_screen = True;
1425+}
1426+
1427+template <typename EVENT_TYPE, typename NATIVE_TYPE>
1428+void initialize_event(XEvent* ev, NATIVE_TYPE* xiev);
1429+
1430+template <>
1431+void initialize_event<XButtonEvent>(XEvent* ev, XIDeviceEvent* xiev)
1432+{
1433+ XButtonEvent* bev = &ev->xbutton;
1434+ ev->type = (xiev->evtype == XI_ButtonPress) ? ButtonPress : ButtonRelease;
1435+ initialize_event_common(bev, xiev);
1436+ bev->button = xiev->detail;
1437+}
1438+
1439+template <>
1440+void initialize_event<XKeyEvent>(XEvent* ev, XIDeviceEvent* xiev)
1441+{
1442+ XKeyEvent* kev = &ev->xkey;
1443+ ev->type = (xiev->evtype == XI_KeyPress) ? KeyPress : KeyRelease;
1444+ initialize_event_common(kev, xiev);
1445+ kev->keycode = xiev->detail;
1446+}
1447+
1448+template <>
1449+void initialize_event<XMotionEvent>(XEvent* ev, XIDeviceEvent* xiev)
1450+{
1451+ XMotionEvent* mev = &ev->xmotion;
1452+ ev->type = MotionNotify;
1453+ initialize_event_common(mev, xiev);
1454+ mev->is_hint = NotifyNormal;
1455+
1456+ for (int i = 0; i < xiev->buttons.mask_len * 8; ++i)
1457+ {
1458+ if (XIMaskIsSet(xiev->buttons.mask, i))
1459+ {
1460+ mev->is_hint = NotifyHint;
1461+ break;
1462+ }
1463+ }
1464+}
1465+
1466+template <>
1467+void initialize_event<XGenericEventCookie>(XEvent* ev, XIBarrierEvent* xiev)
1468+{
1469+ XGenericEventCookie* cev = &ev->xcookie;
1470+ cev->type = GenericEvent;
1471+ cev->serial = xiev->serial;
1472+ cev->send_event = xiev->send_event;
1473+ cev->display = xiev->display;
1474+ cev->evtype = xiev->evtype;
1475+ cev->data = xiev;
1476+}
1477+}
1478+
1479+struct Monitor::Impl
1480+{
1481+ Impl()
1482+ : xi_opcode_(0)
1483+ , event_filter_set_(false)
1484+ , invoking_callbacks_(false)
1485+ {
1486+ Display *dpy = gdk_x11_get_default_xdisplay();
1487+ int event_base, error_base;
1488+
1489+ if (XQueryExtension(dpy, "XInputExtension", &xi_opcode_, &event_base, &error_base))
1490+ {
1491+ int maj = XINPUT_MAJOR_VERSION;
1492+ int min = XINPUT_MINOR_VERSION;
1493+
1494+ if (XIQueryVersion(dpy, &maj, &min) == BadRequest)
1495+ {
1496+ LOG_ERROR(logger) << "Need XInput version "<< maj << "." << min << ", "
1497+ << "impossible, to setup an InputMonitor";
1498+ }
1499+ }
1500+ else
1501+ {
1502+ LOG_ERROR(logger) << "Missing XInput, impossible to setup an InputMonitor";
1503+ }
1504+ }
1505+
1506+ ~Impl()
1507+ {
1508+ if (event_filter_set_)
1509+ {
1510+ pointer_callbacks_.clear();
1511+ key_callbacks_.clear();
1512+ barrier_callbacks_.clear();
1513+ UpdateEventMonitor();
1514+ }
1515+ }
1516+
1517+ bool RegisterClient(Events type, EventCallback const& cb)
1518+ {
1519+ bool added = false;
1520+
1521+ if (type & Events::POINTER)
1522+ added = pointer_callbacks_.insert(cb).second || added;
1523+
1524+ if (type & Events::KEYS)
1525+ added = key_callbacks_.insert(cb).second || added;
1526+
1527+ if (type & Events::BARRIER)
1528+ added = barrier_callbacks_.insert(cb).second || added;
1529+
1530+ if (added)
1531+ UpdateEventMonitor();
1532+
1533+ return added;
1534+ }
1535+
1536+ bool UnregisterClient(EventCallback const& cb)
1537+ {
1538+ if (invoking_callbacks_)
1539+ {
1540+ // Delay the event removal if we're currently invoking a callback
1541+ // not to break the callbacks loop
1542+ removal_queue_.insert(cb);
1543+ return false;
1544+ }
1545+
1546+ bool removed = false;
1547+ removed = pointer_callbacks_.erase(cb) > 0 || removed;
1548+ removed = key_callbacks_.erase(cb) > 0 || removed;
1549+ removed = barrier_callbacks_.erase(cb) > 0 || removed;
1550+
1551+ if (removed)
1552+ UpdateEventMonitor();
1553+
1554+ return removed;
1555+ }
1556+
1557+ Events RegisteredEvents(EventCallback const& cb) const
1558+ {
1559+ Events events = Events::NONE;
1560+
1561+ if (pointer_callbacks_.find(cb) != end(pointer_callbacks_))
1562+ events |= Events::POINTER;
1563+
1564+ if (key_callbacks_.find(cb) != end(key_callbacks_))
1565+ events |= Events::KEYS;
1566+
1567+ if (barrier_callbacks_.find(cb) != end(barrier_callbacks_))
1568+ events |= Events::BARRIER;
1569+
1570+ return events;
1571+ }
1572+
1573+ void UpdateEventMonitor()
1574+ {
1575+ auto* dpy = nux::GetGraphicsDisplay()->GetX11Display();
1576+ Window root = DefaultRootWindow(dpy);
1577+
1578+ unsigned char master_dev_bits[XIMaskLen(XI_LASTEVENT)] = { 0 };
1579+ XIEventMask master_dev = { XIAllMasterDevices, sizeof(master_dev_bits), master_dev_bits };
1580+
1581+ if (!barrier_callbacks_.empty())
1582+ {
1583+ XISetMask(master_dev.mask, XI_BarrierHit);
1584+ XISetMask(master_dev.mask, XI_BarrierLeave);
1585+ }
1586+
1587+ unsigned char all_devs_bits[XIMaskLen(XI_LASTEVENT)] = { 0 };
1588+ XIEventMask all_devs = { XIAllDevices, sizeof(all_devs_bits), all_devs_bits };
1589+
1590+ if (!pointer_callbacks_.empty())
1591+ {
1592+ XISetMask(all_devs.mask, XI_Motion);
1593+ XISetMask(all_devs.mask, XI_ButtonPress);
1594+ XISetMask(all_devs.mask, XI_ButtonRelease);
1595+ }
1596+
1597+ if (!key_callbacks_.empty())
1598+ {
1599+ XISetMask(all_devs.mask, XI_KeyPress);
1600+ XISetMask(all_devs.mask, XI_KeyRelease);
1601+ }
1602+
1603+ XIEventMask selected[] = {master_dev, all_devs};
1604+ XISelectEvents(dpy, root, selected, G_N_ELEMENTS(selected));
1605+ XSync(dpy, False);
1606+
1607+ if (!pointer_callbacks_.empty() || !key_callbacks_.empty() || !barrier_callbacks_.empty())
1608+ {
1609+ if (!event_filter_set_)
1610+ {
1611+ nux::GetGraphicsDisplay()->AddEventFilter({[] (XEvent event, void* data) {
1612+ return static_cast<Impl*>(data)->HandleEvent(event);
1613+ }, this});
1614+
1615+ event_filter_set_ = true;
1616+ }
1617+ }
1618+ else if (event_filter_set_)
1619+ {
1620+ nux::GetGraphicsDisplay()->RemoveEventFilter(this);
1621+ event_filter_set_ = false;
1622+ }
1623+ }
1624+
1625+ bool HandleEvent(XEvent& event)
1626+ {
1627+ bool handled = false;
1628+
1629+ if (event.type != GenericEvent || event.xcookie.extension != xi_opcode_)
1630+ return handled;
1631+
1632+ switch (event.xcookie.evtype)
1633+ {
1634+ case XI_ButtonPress:
1635+ case XI_ButtonRelease:
1636+ handled = InvokeCallbacks<XButtonEvent>(pointer_callbacks_, event);
1637+ break;
1638+ case XI_Motion:
1639+ handled = InvokeCallbacks<XMotionEvent>(pointer_callbacks_, event);
1640+ break;
1641+ case XI_KeyPress:
1642+ case XI_KeyRelease:
1643+ handled = InvokeCallbacks<XKeyEvent>(key_callbacks_, event);
1644+ break;
1645+ case XI_BarrierHit:
1646+ case XI_BarrierLeave:
1647+ handled = InvokeCallbacks<XGenericEventCookie, XIBarrierEvent>(barrier_callbacks_, event);
1648+ break;
1649+ }
1650+
1651+ return handled;
1652+ }
1653+
1654+ template <typename EVENT_TYPE, typename NATIVE_TYPE = XIDeviceEvent>
1655+ bool InvokeCallbacks(std::unordered_set<EventCallback>& callbacks, XEvent& xiev)
1656+ {
1657+ XGenericEventCookie *cookie = &xiev.xcookie;
1658+
1659+ if (!XGetEventData(xiev.xany.display, cookie))
1660+ return false;
1661+
1662+ XEvent event;
1663+ initialize_event<EVENT_TYPE>(&event, reinterpret_cast<NATIVE_TYPE*>(cookie->data));
1664+ invoking_callbacks_ = true;
1665+
1666+ for (auto it = callbacks.begin(); it != callbacks.end();)
1667+ {
1668+ if (it->empty())
1669+ {
1670+ it = callbacks.erase(it);
1671+ continue;
1672+ }
1673+
1674+ (*it)(event);
1675+ ++it;
1676+ }
1677+
1678+ XFreeEventData(xiev.xany.display, cookie);
1679+ invoking_callbacks_ = false;
1680+
1681+ // A callback might unregister itself on the event callback, causing the
1682+ // above callbacks loop to crash, so in this case we save the event in the
1683+ // removal queue and eventually we unregistered these callbacks.
1684+ bool update_event_monitor = false;
1685+ for (auto it = removal_queue_.begin(); it != removal_queue_.end(); it = removal_queue_.erase(it))
1686+ {
1687+ auto const& cb = *it;
1688+ pointer_callbacks_.erase(cb);
1689+ key_callbacks_.erase(cb);
1690+ barrier_callbacks_.erase(cb);
1691+ update_event_monitor = true;
1692+ }
1693+
1694+ if (callbacks.empty() || update_event_monitor)
1695+ {
1696+ idle_removal_.reset(new glib::Idle([this] {
1697+ UpdateEventMonitor();
1698+ return false;
1699+ }));
1700+
1701+ return false;
1702+ }
1703+
1704+ return true;
1705+ }
1706+
1707+ int xi_opcode_;
1708+ bool event_filter_set_;
1709+ bool invoking_callbacks_;
1710+ glib::Source::UniquePtr idle_removal_;
1711+ std::unordered_set<EventCallback> pointer_callbacks_;
1712+ std::unordered_set<EventCallback> key_callbacks_;
1713+ std::unordered_set<EventCallback> barrier_callbacks_;
1714+ std::unordered_set<EventCallback> removal_queue_;
1715+};
1716+
1717+Monitor::Monitor()
1718+{
1719+ if (instance_)
1720+ {
1721+ LOG_WARN(logger) << "More than one input::Monitor created.";
1722+ return;
1723+ }
1724+
1725+ instance_ = this;
1726+ impl_.reset(new Impl());
1727+}
1728+
1729+Monitor::~Monitor()
1730+{
1731+ if (this == instance_)
1732+ instance_ = nullptr;
1733+}
1734+
1735+Monitor& Monitor::Get()
1736+{
1737+ if (!instance_)
1738+ {
1739+ LOG_ERROR(logger) << "No input::Monitor created yet.";
1740+ }
1741+
1742+ return *instance_;
1743+}
1744+
1745+bool Monitor::RegisterClient(Events events, EventCallback const& cb)
1746+{
1747+ return impl_->RegisterClient(events, cb);
1748+}
1749+
1750+bool Monitor::UnregisterClient(EventCallback const& cb)
1751+{
1752+ return impl_->UnregisterClient(cb);
1753+}
1754+
1755+Events Monitor::RegisteredEvents(EventCallback const& cb) const
1756+{
1757+ return impl_->RegisteredEvents(cb);
1758+}
1759+
1760+} // input namespace
1761+} // unity namespace
1762
1763=== added file 'unity-shared/InputMonitor.h'
1764--- unity-shared/InputMonitor.h 1970-01-01 00:00:00 +0000
1765+++ unity-shared/InputMonitor.h 2016-08-30 10:27:02 +0000
1766@@ -0,0 +1,67 @@
1767+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
1768+/*
1769+ * Copyright (C) 2014 Canonical Ltd
1770+ *
1771+ * This program is free software: you can redistribute it and/or modify
1772+ * it under the terms of the GNU General Public License version 3 as
1773+ * published by the Free Software Foundation.
1774+ *
1775+ * This program is distributed in the hope that it will be useful,
1776+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1777+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1778+ * GNU General Public License for more details.
1779+ *
1780+ * You should have received a copy of the GNU General Public License
1781+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1782+ *
1783+ * Authored by: Marco Trevisan <marco.trevisan@canonical.com>
1784+ */
1785+
1786+#ifndef __UNITY_INPUT_MONITOR__
1787+#define __UNITY_INPUT_MONITOR__
1788+
1789+#include <X11/Xlib.h>
1790+#include <sigc++/slot.h>
1791+#include <memory>
1792+
1793+namespace unity
1794+{
1795+namespace input
1796+{
1797+enum class Events : unsigned
1798+{
1799+ NONE = 0,
1800+ POINTER = (1 << 0),
1801+ KEYS = (1 << 1),
1802+ BARRIER = (1 << 2),
1803+ INPUT = POINTER | KEYS,
1804+ ALL = POINTER | KEYS | BARRIER
1805+};
1806+
1807+class Monitor : public sigc::trackable
1808+{
1809+public:
1810+ typedef sigc::slot<void, XEvent const&> EventCallback;
1811+
1812+ static Monitor& Get();
1813+
1814+ Monitor();
1815+ virtual ~Monitor();
1816+
1817+ bool RegisterClient(Events, EventCallback const&);
1818+ bool UnregisterClient(EventCallback const&);
1819+
1820+ Events RegisteredEvents(EventCallback const&) const;
1821+
1822+private:
1823+ Monitor(Monitor const&) = delete;
1824+ Monitor& operator=(Monitor const&) = delete;
1825+
1826+ struct Impl;
1827+ std::unique_ptr<Impl> impl_;
1828+};
1829+
1830+} // input namespace
1831+} // unity namespace
1832+
1833+#endif // __UNITY_INPUT_MONITOR__
1834
1835=== modified file 'unity-shared/MenuManager.cpp'
1836--- unity-shared/MenuManager.cpp 2015-10-02 14:02:05 +0000
1837+++ unity-shared/MenuManager.cpp 2016-08-30 10:27:02 +0000
1838@@ -21,11 +21,16 @@
1839 #include <gtk/gtk.h>
1840 #include <NuxCore/Logger.h>
1841 #include <UnityCore/GLibSignal.h>
1842+#include <UnityCore/GLibSource.h>
1843 #include <UnityCore/GLibWrapper.h>
1844 #include <UnityCore/DBusIndicators.h>
1845 #include <unordered_map>
1846
1847 #include "MenuManager.h"
1848+#include "InputMonitor.h"
1849+#include "RawPixel.h"
1850+#include "UnitySettings.h"
1851+#include "UScreen.h"
1852 #include "WindowManager.h"
1853
1854 namespace unity
1855@@ -40,6 +45,10 @@
1856 const std::string LIM_KEY = "integrated-menus";
1857 const std::string SHOW_MENUS_NOW_DELAY = "show-menus-now-delay";
1858 const std::string ALWAYS_SHOW_MENUS_KEY = "always-show-menus";
1859+
1860+const RawPixel TRIANGLE_THRESHOLD = 5_em;
1861+const double SCRUB_VELOCITY_THRESHOLD = 0.05;
1862+const unsigned MENU_OPEN_MOUSE_WAIT = 150;
1863 }
1864
1865 using namespace indicator;
1866@@ -51,6 +60,7 @@
1867 , indicators_(indicators)
1868 , key_grabber_(grabber)
1869 , show_now_window_(0)
1870+ , last_pointer_time_(0)
1871 , settings_(g_settings_new(SETTINGS_NAME.c_str()))
1872 {
1873 for (auto const& indicator : indicators_->GetIndicators())
1874@@ -182,9 +192,15 @@
1875 parent_->key_activate_entry.emit(entry_id);
1876 }
1877
1878- void EntryActivated(std::string const&, std::string const&, nux::Rect const& geo)
1879+ void EntryActivated(std::string const& menubar, std::string const&, nux::Rect const& geo)
1880 {
1881 parent_->menu_open = !geo.IsNull();
1882+
1883+ if (active_menubar_ != menubar)
1884+ {
1885+ active_menubar_ = menubar;
1886+ UpdateActiveTracker();
1887+ }
1888 }
1889
1890 void SetShowNowForWindow(Window xid, bool show)
1891@@ -231,15 +247,147 @@
1892 gtk_icon_theme_set_search_path(gtk_icon_theme_get_default(), gicon_paths.data(), gicon_paths.size());
1893 }
1894
1895+ bool PointInTriangle(nux::Point const& p, nux::Point const& t0, nux::Point const& t1, nux::Point const& t2)
1896+ {
1897+ int s = t0.y * t2.x - t0.x * t2.y + (t2.y - t0.y) * p.x + (t0.x - t2.x) * p.y;
1898+ int t = t0.x * t1.y - t0.y * t1.x + (t0.y - t1.y) * p.x + (t1.x - t0.x) * p.y;
1899+
1900+ if ((s < 0) != (t < 0))
1901+ return false;
1902+
1903+ int A = -t1.y * t2.x + t0.y * (t2.x - t1.x) + t0.x * (t1.y - t2.y) + t1.x * t2.y;
1904+ if (A < 0)
1905+ {
1906+ s = -s;
1907+ t = -t;
1908+ A = -A;
1909+ }
1910+
1911+ return s > 0 && t > 0 && (s + t) < A;
1912+ }
1913+
1914+ double GetMouseVelocity(nux::Point const& p0, nux::Point const& p1, Time time_delta)
1915+ {
1916+ int dx, dy;
1917+ double speed;
1918+
1919+ if (time_delta == 0)
1920+ return 1;
1921+
1922+ dx = p0.x - p1.x;
1923+ dy = p0.y - p1.y;
1924+
1925+ speed = sqrt(dx * dx + dy * dy) / time_delta;
1926+
1927+ return speed;
1928+ }
1929+
1930+ void OnActiveEntryEvent(XEvent const& e)
1931+ {
1932+ if (e.type != MotionNotify)
1933+ return;
1934+
1935+ auto const& active_entry = indicators_->GetActiveEntry();
1936+
1937+ if (!active_entry)
1938+ return;
1939+
1940+ nux::Point mouse(e.xmotion.x_root, e.xmotion.y_root);
1941+ auto monitor = UScreen::GetDefault()->GetMonitorAtPosition(mouse.x, mouse.y);
1942+ double scale = Settings::Instance().em(monitor)->DPIScale();
1943+ double speed = GetMouseVelocity(mouse, tracked_pointer_pos_, e.xmotion.time - last_pointer_time_);
1944+ auto menu_geo = active_entry->geometry();
1945+
1946+ tracked_pointer_pos_ = mouse;
1947+ last_pointer_time_ = e.xmotion.time;
1948+
1949+ if (speed > SCRUB_VELOCITY_THRESHOLD &&
1950+ PointInTriangle(mouse, {mouse.x, std::max(mouse.y - TRIANGLE_THRESHOLD.CP(scale), 0)},
1951+ menu_geo.GetPosition(), {menu_geo.x + menu_geo.width, menu_geo.y}))
1952+ {
1953+ pointer_movement_timeout_ = std::make_shared<glib::Timeout>(MENU_OPEN_MOUSE_WAIT, [this, mouse, speed] {
1954+ if (active_tracker_)
1955+ active_tracker_(mouse.x, mouse.y, speed);
1956+
1957+ return false;
1958+ });
1959+
1960+ return;
1961+ }
1962+
1963+ if (active_tracker_)
1964+ {
1965+ pointer_movement_timeout_.reset();
1966+ active_tracker_(mouse.x, mouse.y, speed);
1967+ }
1968+ }
1969+
1970+ bool RegisterTracker(std::string const& menubar, PositionTracker const& cb)
1971+ {
1972+ auto it = position_trackers_.find(menubar);
1973+
1974+ if (it != end(position_trackers_))
1975+ return false;
1976+
1977+ position_trackers_.insert({menubar, cb});
1978+
1979+ if (active_menubar_ == menubar)
1980+ UpdateActiveTracker();
1981+
1982+ return true;
1983+ }
1984+
1985+ bool UnregisterTracker(std::string const& menubar, PositionTracker const& cb)
1986+ {
1987+ auto it = position_trackers_.find(menubar);
1988+
1989+ if (it == end(position_trackers_))
1990+ return false;
1991+
1992+ if (!cb || (cb && it->second == cb))
1993+ {
1994+ position_trackers_.erase(it);
1995+ return true;
1996+ }
1997+
1998+ return false;
1999+ }
2000+
2001+ void UpdateActiveTracker()
2002+ {
2003+ auto it = position_trackers_.find(active_menubar_);
2004+ active_tracker_ = (it != end(position_trackers_)) ? it->second : PositionTracker();
2005+ pointer_movement_timeout_.reset();
2006+
2007+ if (active_tracker_)
2008+ {
2009+ if (input::Monitor::Get().RegisterClient(input::Events::POINTER, sigc::mem_fun(this, &Impl::OnActiveEntryEvent)))
2010+ last_pointer_time_ = 0;
2011+ }
2012+ else
2013+ {
2014+ input::Monitor::Get().UnregisterClient(sigc::mem_fun(this, &Impl::OnActiveEntryEvent));
2015+
2016+ if (it != end(position_trackers_))
2017+ position_trackers_.erase(it);
2018+ }
2019+ }
2020+
2021 Manager* parent_;
2022 Indicators::Ptr indicators_;
2023 AppmenuIndicator::Ptr appmenu_;
2024 key::Grabber::Ptr key_grabber_;
2025 Window show_now_window_;
2026+ std::string active_menubar_;
2027+ PositionTracker active_tracker_;
2028+ nux::Point tracked_pointer_pos_;
2029+ Time last_pointer_time_;
2030+ glib::Source::Ptr pointer_movement_timeout_;
2031 connection::Manager appmenu_connections_;
2032 connection::Wrapper active_win_conn_;
2033 glib::Object<GSettings> settings_;
2034 glib::SignalManager signals_;
2035+ std::unordered_map<std::string, PositionTracker> position_trackers_;
2036 std::unordered_map<indicator::Entry::Ptr, uint32_t> entry_actions_;
2037 };
2038
2039@@ -278,5 +426,16 @@
2040 return impl_->key_grabber_;
2041 }
2042
2043+bool Manager::RegisterTracker(std::string const& menubar, PositionTracker const& cb)
2044+{
2045+ return impl_->RegisterTracker(menubar, cb);
2046+}
2047+
2048+bool Manager::UnregisterTracker(std::string const& menubar, PositionTracker const& cb)
2049+{
2050+ return impl_->UnregisterTracker(menubar, cb);
2051+}
2052+
2053+
2054 } // menu namespace
2055 } // unity namespace
2056
2057=== modified file 'unity-shared/MenuManager.h'
2058--- unity-shared/MenuManager.h 2015-06-05 14:28:27 +0000
2059+++ unity-shared/MenuManager.h 2016-08-30 10:27:02 +0000
2060@@ -67,6 +67,10 @@
2061
2062 key::Grabber::Ptr const& KeyGrabber() const;
2063
2064+ typedef sigc::slot<void, int /*x*/, int /*y*/, double /*speed*/> PositionTracker;
2065+ bool RegisterTracker(std::string const& menubar, PositionTracker const&);
2066+ bool UnregisterTracker(std::string const& menubar, PositionTracker const& = PositionTracker());
2067+
2068 sigc::signal<void> appmenu_added;
2069 sigc::signal<void> appmenu_removed;
2070 sigc::signal<bool>::accumulated<any_true> open_first;
2071
2072=== added file 'unity-shared/SigcSlotHash.h'
2073--- unity-shared/SigcSlotHash.h 1970-01-01 00:00:00 +0000
2074+++ unity-shared/SigcSlotHash.h 2016-08-30 10:27:02 +0000
2075@@ -0,0 +1,64 @@
2076+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
2077+/*
2078+ * Copyright (C) 2016 Canonical Ltd
2079+ *
2080+ * This program is free software: you can redistribute it and/or modify
2081+ * it under the terms of the GNU General Public License version 3 as
2082+ * published by the Free Software Foundation.
2083+ *
2084+ * This program is distributed in the hope that it will be useful,
2085+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2086+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2087+ * GNU General Public License for more details.
2088+ *
2089+ * You should have received a copy of the GNU General Public License
2090+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2091+ *
2092+ * Authored by: Marco Trevisan <marco.trevisan@canonical.com>
2093+ */
2094+
2095+#ifndef __UNITY_SIGC_SLOT_HASHER__
2096+#define __UNITY_SIGC_SLOT_HASHER__
2097+
2098+#include <sigc++/slot.h>
2099+
2100+namespace std
2101+{
2102+
2103+template<>
2104+struct hash<sigc::slot_base>
2105+{
2106+ size_t operator()(sigc::slot_base const& cb) const
2107+ {
2108+ if (cb.rep_)
2109+ return hash<size_t>()(reinterpret_cast<size_t>(cb.rep_->call_));
2110+
2111+ return hash<size_t>()(reinterpret_cast<size_t>(cb.rep_));
2112+ }
2113+};
2114+
2115+template<>
2116+struct equal_to<sigc::slot_base>
2117+{
2118+ bool operator()(sigc::slot_base const& lhs, sigc::slot_base const& rhs) const
2119+ {
2120+ if (!lhs.rep_ || !rhs.rep_)
2121+ return (lhs.rep_ == rhs.rep_);
2122+
2123+ return (lhs.rep_->call_ == rhs.rep_->call_);
2124+ }
2125+};
2126+
2127+template<class T>
2128+struct hash
2129+{
2130+ size_t operator()(T const& cb) const
2131+ {
2132+ static_assert(std::is_base_of<sigc::slot_base, T>::value, "Type is not derived from sigc::slot_base");
2133+ return hash<sigc::slot_base>()(cb);
2134+ }
2135+};
2136+
2137+} // std namespace
2138+
2139+#endif // __UNITY_SIGC_SLOT_HASHER__
2140
2141=== modified file 'unity-shared/StandaloneWindowManager.cpp'
2142--- unity-shared/StandaloneWindowManager.cpp 2015-11-20 11:33:38 +0000
2143+++ unity-shared/StandaloneWindowManager.cpp 2016-08-30 10:27:02 +0000
2144@@ -624,6 +624,9 @@
2145 return std::string();
2146 }
2147
2148+void StandaloneWindowManager::SetCardinalProperty(Window, Atom, std::vector<long> const&)
2149+{}
2150+
2151 std::vector<long> StandaloneWindowManager::GetCardinalProperty(Window, Atom) const
2152 {
2153 return std::vector<long>();
2154
2155=== modified file 'unity-shared/StandaloneWindowManager.h'
2156--- unity-shared/StandaloneWindowManager.h 2016-03-18 18:58:26 +0000
2157+++ unity-shared/StandaloneWindowManager.h 2016-08-30 10:27:02 +0000
2158@@ -165,6 +165,7 @@
2159 virtual std::string GetWindowName(Window window_id) const;
2160 virtual bool IsOnscreenKeyboard(Window window_id) const;
2161 virtual std::string GetStringProperty(Window window_id, Atom) const;
2162+ virtual void SetCardinalProperty(Window window_id, Atom, std::vector<long> const&);
2163 virtual std::vector<long> GetCardinalProperty(Window window_id, Atom) const;
2164
2165 // Mock functions
2166
2167=== modified file 'unity-shared/WindowManager.h'
2168--- unity-shared/WindowManager.h 2016-03-18 18:58:26 +0000
2169+++ unity-shared/WindowManager.h 2016-08-30 10:27:02 +0000
2170@@ -170,6 +170,7 @@
2171 virtual bool IsOnscreenKeyboard(Window window_id) const = 0;
2172
2173 virtual std::string GetStringProperty(Window, Atom) const = 0;
2174+ virtual void SetCardinalProperty(Window, Atom, std::vector<long> const&) = 0;
2175 virtual std::vector<long> GetCardinalProperty(Window, Atom) const = 0;
2176
2177 virtual Cursor GetCachedCursor(unsigned int cursor_name) const = 0;
2178
2179=== modified file 'unity-shared/XWindowManager.cpp'
2180--- unity-shared/XWindowManager.cpp 2015-01-21 15:28:59 +0000
2181+++ unity-shared/XWindowManager.cpp 2016-08-30 10:27:02 +0000
2182@@ -123,6 +123,12 @@
2183 return std::string(val, n_items);
2184 }
2185
2186+void XWindowManager::SetCardinalProperty(Window window_id, Atom atom, std::vector<long> const& values)
2187+{
2188+ XChangeProperty(screen->dpy(), window_id, atom, XA_CARDINAL, 32, PropModeReplace,
2189+ (unsigned char *) values.data(), values.size());
2190+}
2191+
2192 std::vector<long> XWindowManager::GetCardinalProperty(Window window_id, Atom atom) const
2193 {
2194 Atom type;
2195
2196=== modified file 'unity-shared/XWindowManager.h'
2197--- unity-shared/XWindowManager.h 2015-01-21 15:28:59 +0000
2198+++ unity-shared/XWindowManager.h 2016-08-30 10:27:02 +0000
2199@@ -36,6 +36,7 @@
2200 std::string GetWindowName(Window window_id) const;
2201 bool IsOnscreenKeyboard(Window window_id) const;
2202 std::string GetStringProperty(Window window_id, Atom atom) const;
2203+ void SetCardinalProperty(Window, Atom, std::vector<long> const&);
2204 std::vector<long> GetCardinalProperty(Window, Atom) const;
2205 };
2206