summaryrefslogtreecommitdiff
path: root/unity-shared
diff options
Diffstat (limited to 'unity-shared')
-rw-r--r--unity-shared/CMakeLists.txt1
-rw-r--r--unity-shared/InputMonitor.cpp420
-rw-r--r--unity-shared/InputMonitor.h67
-rw-r--r--unity-shared/MenuManager.cpp163
-rw-r--r--unity-shared/MenuManager.h4
-rw-r--r--unity-shared/PluginAdapter.cpp2
-rw-r--r--unity-shared/SigcSlotHash.h70
-rw-r--r--unity-shared/StandaloneWindowManager.cpp3
-rw-r--r--unity-shared/StandaloneWindowManager.h1
-rw-r--r--unity-shared/UpstartWrapper.cpp2
-rw-r--r--unity-shared/WindowButtons.cpp15
-rw-r--r--unity-shared/WindowManager.h1
-rw-r--r--unity-shared/XWindowManager.cpp6
-rw-r--r--unity-shared/XWindowManager.h1
14 files changed, 752 insertions, 4 deletions
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;
};