gh-13108: Make compact mode track mouse moves outside of window (gh-14818)

This commit is contained in:
mr. m
2026-08-02 20:24:58 +02:00
committed by GitHub
parent ccbd934482
commit 9714c15c7a
12 changed files with 759 additions and 6 deletions

View File

@@ -20,6 +20,14 @@
- name: zen.view.compact.sidebar-keep-hover.duration
value: 150
# How far (in CSS pixels) the mouse may travel past the window bounds after
# leaving the window before the hovered sidebar/toolbar is collapsed
- name: zen.view.compact.outside-window-edge-offset.horizontal
value: 250
- name: zen.view.compact.outside-window-edge-offset.vertical
value: 150
- name: zen.view.compact.animate-sidebar
value: true

View File

@@ -34,6 +34,29 @@ XPCOMUtils.defineLazyPreferenceGetter(
true
);
// Distance (in CSS pixels) the mouse can travel past the window bounds after
// leaving the window before the hovered element is collapsed
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"COMPACT_MODE_OUTSIDE_WINDOW_HORIZONTAL_OFFSET",
"zen.view.compact.outside-window-edge-offset.horizontal",
250
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"COMPACT_MODE_OUTSIDE_WINDOW_VERTICAL_OFFSET",
"zen.view.compact.outside-window-edge-offset.vertical",
150
);
XPCOMUtils.defineLazyServiceGetter(
lazy,
"zenMouseTracker",
"@mozilla.org/zen/mouse-tracker;1",
Ci.nsIZenMouseTracker
);
ChromeUtils.defineLazyGetter(lazy, "mainAppWrapper", () =>
document.getElementById("zen-main-app-wrapper")
);
@@ -71,9 +94,21 @@ window.gZenCompactModeManager = {
tabIsRightObserver
);
const outsideMouseTrackerExitObserver =
this._onOutsideMouseTrackerExit.bind(this);
Services.obs.addObserver(
outsideMouseTrackerExitObserver,
"zen-mouse-tracker:exited"
);
window.addEventListener(
"unload",
() => {
this._stopTrackingMouseOutsideWindow();
Services.obs.removeObserver(
outsideMouseTrackerExitObserver,
"zen-mouse-tracker:exited"
);
Services.prefs.removeObserver(
"zen.tabs.vertical.right-side",
tabIsRightObserver
@@ -94,6 +129,10 @@ window.gZenCompactModeManager = {
this._clearAllHoverStates()
);
// Hide any element kept open by the outside mouse tracking as soon as the
// window loses focus
window.addEventListener("deactivate", () => this._collapseTrackedElement());
this._canShowBackgroundTabToast = Services.prefs.getBoolPref(
"zen.view.compact.show-background-tab-toast",
true
@@ -149,6 +188,7 @@ window.gZenCompactModeManager = {
return;
}
delete this._isTabBeingDragged;
this._stopTrackingMouseOutsideWindow();
this.sidebar.removeAttribute("zen-user-show");
// We use this element in order to make it persis across restarts, by using the XULStore.
// main-window can't store attributes other than window sizes, so we use this instead
@@ -723,6 +763,9 @@ window.gZenCompactModeManager = {
}
} else {
if (attr === "zen-has-hover") {
if (element === this._outsideTrackedElement) {
this._stopTrackingMouseOutsideWindow();
}
element.removeAttribute("zen-has-implicit-hover");
gURLBar.updateTextOverflow();
}
@@ -886,18 +929,23 @@ window.gZenCompactModeManager = {
}
window.cancelAnimationFrame(this._removeHoverFrames[target.id]);
this.flashElement(
target,
this.hideAfterHoverDuration,
"has-hover" + target.id,
"zen-has-hover"
);
if (!this._trackMouseOutsideWindow(entry.screenEdge, target)) {
// We can't track the mouse position outside of the window on
// this platform, fall back to hiding after a fixed duration
this.flashElement(
target,
this.hideAfterHoverDuration,
"has-hover" + target.id,
"zen-has-hover"
);
}
document.addEventListener(
"mousemove",
() => {
if (target.matches(":hover")) {
return;
}
// Closing the element also stops the outside mouse tracking
this._setElementExpandAttribute(target, false);
this.clearFlashTimeout("has-hover" + target.id);
},
@@ -941,7 +989,55 @@ window.gZenCompactModeManager = {
return bBox.left - error < x && x < bBox.right + error;
},
_trackMouseOutsideWindow(screenEdge, target) {
this._stopTrackingMouseOutsideWindow();
const maxEdgeOffset =
screenEdge === "left" || screenEdge === "right"
? lazy.COMPACT_MODE_OUTSIDE_WINDOW_HORIZONTAL_OFFSET
: lazy.COMPACT_MODE_OUTSIDE_WINDOW_VERTICAL_OFFSET;
try {
lazy.zenMouseTracker.registerWindow(window, screenEdge, maxEdgeOffset);
} catch (e) {
// The platform can't track the global mouse position (e.g. Wayland)
return false;
}
this._outsideTrackedElement = target;
this.clearFlashTimeout("has-hover" + target.id);
window.requestAnimationFrame(() => {
if (this._outsideTrackedElement === target) {
this._setElementExpandAttribute(target, true);
}
});
return true;
},
_stopTrackingMouseOutsideWindow() {
const target = this._outsideTrackedElement;
if (!target) {
return;
}
this._outsideTrackedElement = null;
lazy.zenMouseTracker.unregisterWindow(window);
},
_collapseTrackedElement() {
const target = this._outsideTrackedElement;
if (!target) {
return;
}
// Closing the element also unregisters us from the mouse tracker
this._setElementExpandAttribute(target, false);
this.clearFlashTimeout("has-hover" + target.id);
},
_onOutsideMouseTrackerExit(subject) {
if (subject === window) {
this._collapseTrackedElement();
}
},
_clearAllHoverStates() {
this._stopTrackingMouseOutsideWindow();
// Clear hover attributes from all hoverable elements
for (let entry of this.hoverableElements) {
const target = entry.element;

View File

@@ -0,0 +1,245 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ZenMouseTracker.h"
#include "ZenMouseTrackerInternal.h"
#include "mozilla/Atomic.h"
#include "mozilla/Services.h"
#include "mozilla/WidgetUtils.h"
#include "nsIObserverService.h"
#include "nsIWidget.h"
#include "nsThreadUtils.h"
namespace zen {
NS_IMPL_ISUPPORTS(ZenMouseTracker, nsIZenMouseTracker, nsIObserver)
static ZenMouseTracker* sInstance = nullptr;
// Pointer moves are coalesced: the backends may report them faster than we
// want to run the checks, and on Windows they arrive from a low level hook
// where as little work as possible should happen. These are atomic since
// some backends (e.g. the macOS global event monitor) may deliver the moves
// off the main thread.
static mozilla::Atomic<int32_t> sLatestPointerX(0);
static mozilla::Atomic<int32_t> sLatestPointerY(0);
static mozilla::Atomic<bool> sPendingEvaluation(false);
ZenMouseTracker::ZenMouseTracker() {
MOZ_ASSERT(NS_IsMainThread());
sInstance = this;
}
ZenMouseTracker::~ZenMouseTracker() {
ZenNativeMouseMonitor::Stop();
if (sInstance == this) {
sInstance = nullptr;
}
}
// static
mozilla::Maybe<ZenMouseTracker::TrackedEdge> ZenMouseTracker::ParseEdge(
const nsACString& aScreenEdge) {
if (aScreenEdge.EqualsLiteral("left")) {
return mozilla::Some(TrackedEdge::Left);
}
if (aScreenEdge.EqualsLiteral("right")) {
return mozilla::Some(TrackedEdge::Right);
}
if (aScreenEdge.EqualsLiteral("top")) {
return mozilla::Some(TrackedEdge::Top);
}
if (aScreenEdge.EqualsLiteral("bottom")) {
return mozilla::Some(TrackedEdge::Bottom);
}
return mozilla::Nothing();
}
NS_IMETHODIMP
ZenMouseTracker::RegisterWindow(mozIDOMWindowProxy* aWindow,
const nsACString& aScreenEdge,
float aMaxEdgeOffset) {
MOZ_ASSERT(NS_IsMainThread());
const auto edge = ParseEdge(aScreenEdge);
if (!aWindow || edge.isNothing() || aMaxEdgeOffset < 0.0f) {
return NS_ERROR_INVALID_ARG;
}
nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
if (!window) {
return NS_ERROR_INVALID_ARG;
}
nsresult rv = ZenNativeMouseMonitor::Start();
if (NS_FAILED(rv)) {
return rv;
}
for (auto& tracked : mTracked) {
if (tracked.mWindow == window) {
tracked.mEdge = *edge;
tracked.mMaxEdgeOffset = aMaxEdgeOffset;
return NS_OK;
}
}
mTracked.AppendElement(TrackedWindow{window, *edge, aMaxEdgeOffset});
AddObservers();
return NS_OK;
}
void ZenMouseTracker::AddObservers() {
if (mObserving) {
return;
}
if (nsCOMPtr<nsIObserverService> obs =
mozilla::services::GetObserverService()) {
obs->AddObserver(this, "domwindowclosed", false);
obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
mObserving = true;
}
}
void ZenMouseTracker::RemoveObservers() {
if (!mObserving) {
return;
}
if (nsCOMPtr<nsIObserverService> obs =
mozilla::services::GetObserverService()) {
obs->RemoveObserver(this, "domwindowclosed");
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
}
mObserving = false;
}
NS_IMETHODIMP
ZenMouseTracker::UnregisterWindow(mozIDOMWindowProxy* aWindow) {
MOZ_ASSERT(NS_IsMainThread());
if (!aWindow) {
return NS_ERROR_INVALID_ARG;
}
StopTrackingWindow(nsPIDOMWindowOuter::From(aWindow));
return NS_OK;
}
void ZenMouseTracker::StopTrackingWindow(nsPIDOMWindowOuter* aWindow) {
for (size_t i = 0; i < mTracked.Length(); i++) {
if (mTracked[i].mWindow == aWindow) {
mTracked.RemoveElementAt(i);
break;
}
}
OnTrackedListChanged();
}
void ZenMouseTracker::OnTrackedListChanged() {
if (mTracked.IsEmpty()) {
ZenNativeMouseMonitor::Stop();
RemoveObservers();
}
}
// static
void ZenMouseTracker::OnNativePointerMove(const mozilla::DesktopPoint& aPoint) {
sLatestPointerX = int32_t(aPoint.x);
sLatestPointerY = int32_t(aPoint.y);
if (sPendingEvaluation.exchange(true)) {
// An already queued evaluation will pick up the position we just stored
return;
}
nsresult rv = NS_DispatchToMainThread(
NS_NewRunnableFunction("zen::ZenMouseTracker::Evaluate", [] {
sPendingEvaluation = false;
if (RefPtr<ZenMouseTracker> tracker = sInstance) {
tracker->Evaluate(mozilla::DesktopPoint(float(sLatestPointerX),
float(sLatestPointerY)));
}
}));
if (NS_FAILED(rv)) {
// Don't let a failed dispatch (e.g. during shutdown) block all future
// evaluations
sPendingEvaluation = false;
}
}
void ZenMouseTracker::Evaluate(const mozilla::DesktopPoint& aPoint) {
nsTArray<nsCOMPtr<nsPIDOMWindowOuter>> exited;
for (size_t i = mTracked.Length(); i > 0; i--) {
auto& tracked = mTracked[i - 1];
RefPtr<nsIWidget> widget =
mozilla::widget::WidgetUtils::DOMWindowToWidget(tracked.mWindow);
if (!widget) {
mTracked.RemoveElementAt(i - 1);
continue;
}
if (!IsPointerWithinBounds(tracked, aPoint, widget)) {
exited.AppendElement(std::move(tracked.mWindow));
mTracked.RemoveElementAt(i - 1);
}
}
OnTrackedListChanged();
if (exited.IsEmpty()) {
return;
}
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs) {
return;
}
for (auto& window : exited) {
obs->NotifyObservers(window, ZEN_MOUSE_TRACKER_EXITED_TOPIC, nullptr);
}
}
// static
bool ZenMouseTracker::IsPointerWithinBounds(const TrackedWindow& aTracked,
const mozilla::DesktopPoint& aPoint,
nsIWidget* aWidget) {
const auto scale = aWidget->GetDesktopToDeviceScale();
const float x = aPoint.x * scale.scale;
const float y = aPoint.y * scale.scale;
const auto origin = aWidget->WidgetToScreenOffset();
const auto size = aWidget->GetClientSize();
const float left = origin.x;
const float top = origin.y;
const float right = left + size.width;
const float bottom = top + size.height;
if (x >= left && x <= right && y >= top && y <= bottom) {
// Back inside the window; the in-window hover logic owns this case
return true;
}
const float maxOffset =
aTracked.mMaxEdgeOffset * float(aWidget->GetDefaultScale().scale);
switch (aTracked.mEdge) {
case TrackedEdge::Left:
return x >= left - maxOffset && x < left && y >= top && y <= bottom;
case TrackedEdge::Right:
return x > right && x <= right + maxOffset && y >= top && y <= bottom;
case TrackedEdge::Top:
return y >= top - maxOffset && y < top && x >= left && x <= right;
case TrackedEdge::Bottom:
return y > bottom && y <= bottom + maxOffset && x >= left && x <= right;
}
return false;
}
NS_IMETHODIMP
ZenMouseTracker::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) {
if (!strcmp(aTopic, "domwindowclosed")) {
if (nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryInterface(aSubject)) {
StopTrackingWindow(window);
}
return NS_OK;
}
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
mTracked.Clear();
OnTrackedListChanged();
}
return NS_OK;
}
} // namespace zen

View File

@@ -0,0 +1,92 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_ZenMouseTracker_h_
#define mozilla_ZenMouseTracker_h_
#include "nsIZenMouseTracker.h"
#include "mozilla/Maybe.h"
#include "nsIObserver.h"
#include "nsCOMPtr.h"
#include "nsPIDOMWindow.h"
#include "nsTArray.h"
#include "Units.h"
class nsIWidget;
// Fired with the tracked window as subject once the pointer breaks the
// tracking conditions; tracking for that window has already stopped.
#define ZEN_MOUSE_TRACKER_EXITED_TOPIC "zen-mouse-tracker:exited"
namespace zen {
/**
* @brief Watches the global OS pointer position for registered windows and
* notifies observers once the pointer moves too far away from the window
* edge it left through.
*/
class ZenMouseTracker final : public nsIZenMouseTracker, public nsIObserver {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIZENMOUSETRACKER
NS_DECL_NSIOBSERVER
ZenMouseTracker();
/**
* @brief Called by the platform backends whenever the OS pointer moves.
* @param aPoint The pointer position in desktop pixels, relative to the
* origin of the (primary) screen. May be called outside of a clean event
* loop iteration (e.g. from a low level hook), evaluation is coalesced
* onto the main thread.
*/
static void OnNativePointerMove(const mozilla::DesktopPoint& aPoint);
private:
~ZenMouseTracker();
enum class TrackedEdge : uint8_t { Left, Right, Top, Bottom };
struct TrackedWindow {
nsCOMPtr<nsPIDOMWindowOuter> mWindow;
TrackedEdge mEdge;
float mMaxEdgeOffset; // In CSS pixels
};
static mozilla::Maybe<TrackedEdge> ParseEdge(const nsACString& aScreenEdge);
/**
* @brief Check every tracked window against the given pointer position and
* notify + stop tracking the ones the pointer moved too far away from.
*/
void Evaluate(const mozilla::DesktopPoint& aPoint);
/**
* @brief Whether the pointer is still allowed to keep the window's edge
* element open, given the window's bounds on screen.
*/
static bool IsPointerWithinBounds(const TrackedWindow& aTracked,
const mozilla::DesktopPoint& aPoint,
nsIWidget* aWidget);
void StopTrackingWindow(nsPIDOMWindowOuter* aWindow);
/**
* @brief Tear down the native monitor and our observers once the last
* tracked window is gone, so the service is fully idle while nothing is
* being tracked.
*/
void OnTrackedListChanged();
void AddObservers();
void RemoveObservers();
nsTArray<TrackedWindow> mTracked;
bool mObserving = false;
};
} // namespace zen
#endif

View File

@@ -0,0 +1,65 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ZenMouseTracker.h"
#include "ZenMouseTrackerInternal.h"
#include "nsCocoaUtils.h"
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
namespace zen {
// The global monitor only sees events delivered to other applications, so a
// local monitor is needed as well for moves that AppKit routes to our own
// app while the pointer is outside of the tracked window (AppKit sends mouse
// moved events to the key window regardless of the pointer position).
static id sLocalMonitor = nil;
static id sGlobalMonitor = nil;
static void ReportPointerPosition() {
NSPoint point = [NSEvent mouseLocation];
point.y = nsCocoaUtils::FlippedScreenY(point.y);
ZenMouseTracker::OnNativePointerMove(
mozilla::DesktopPoint(float(point.x), float(point.y)));
}
nsresult ZenNativeMouseMonitor::Start() {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
if (sLocalMonitor || sGlobalMonitor) {
return NS_OK;
}
const NSEventMask mask = NSEventMaskMouseMoved | NSEventMaskLeftMouseDragged |
NSEventMaskRightMouseDragged |
NSEventMaskOtherMouseDragged;
sLocalMonitor =
[NSEvent addLocalMonitorForEventsMatchingMask:mask
handler:^(NSEvent* aEvent) {
ReportPointerPosition();
return aEvent;
}];
sGlobalMonitor =
[NSEvent addGlobalMonitorForEventsMatchingMask:mask
handler:^(NSEvent* aEvent) {
ReportPointerPosition();
}];
return NS_OK;
NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_NOT_AVAILABLE);
}
void ZenNativeMouseMonitor::Stop() {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
if (sLocalMonitor) {
[NSEvent removeMonitor:sLocalMonitor];
sLocalMonitor = nil;
}
if (sGlobalMonitor) {
[NSEvent removeMonitor:sGlobalMonitor];
sGlobalMonitor = nil;
}
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
} // namespace zen

View File

@@ -0,0 +1,54 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ZenMouseTracker.h"
#include "ZenMouseTrackerInternal.h"
#include "mozilla/WidgetUtilsGtk.h"
#include <gdk/gdk.h>
#include <glib.h>
namespace zen {
// X11 has no way to observe global pointer motion events without pulling in
// extra library dependencies (XInput2 raw events need libXi, XRecord needs
// libXtst), so we poll the pointer from a GLib timeout instead. This only
// runs during the short periods a window is registered, i.e. while the
// pointer is outside of the window with an edge element held open.
static guint sTimerId = 0;
static gboolean OnTimer(gpointer) {
GdkDisplay* display = gdk_display_get_default();
GdkSeat* seat = display ? gdk_display_get_default_seat(display) : nullptr;
GdkDevice* pointer = seat ? gdk_seat_get_pointer(seat) : nullptr;
if (pointer) {
gint x = 0, y = 0;
gdk_device_get_position(pointer, nullptr, &x, &y);
ZenMouseTracker::OnNativePointerMove(
mozilla::DesktopPoint(float(x), float(y)));
}
return G_SOURCE_CONTINUE;
}
nsresult ZenNativeMouseMonitor::Start() {
if (!mozilla::widget::GdkIsX11Display()) {
// Wayland doesn't expose the pointer position while it is outside of our
// surfaces
return NS_ERROR_NOT_AVAILABLE;
}
if (!sTimerId) {
sTimerId = g_timeout_add(1000 / 60, OnTimer, nullptr);
}
return NS_OK;
}
void ZenNativeMouseMonitor::Stop() {
if (sTimerId) {
g_source_remove(sTimerId);
sTimerId = 0;
}
}
} // namespace zen

View File

@@ -0,0 +1,45 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_ZenMouseTrackerInternal_h_
#define mozilla_ZenMouseTrackerInternal_h_
#include "nscore.h"
#if defined(XP_MACOSX) || defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
# define NS_ZEN_CAN_TRACK_POINTER 1
#endif
namespace zen {
/**
* @brief Platform backend that delivers global pointer movements to
* ZenMouseTracker::OnNativePointerMove, including movements outside of any
* of our windows.
*/
class ZenNativeMouseMonitor final {
public:
#ifdef NS_ZEN_CAN_TRACK_POINTER
/**
* @brief Start delivering pointer moves. Safe to call while already
* started.
* @throws NS_ERROR_NOT_AVAILABLE when the platform cannot observe the
* global pointer (e.g. on Wayland).
*/
static nsresult Start();
/**
* @brief Stop delivering pointer moves. Safe to call while stopped.
*/
static void Stop();
#else
static nsresult Start() { return NS_ERROR_NOT_AVAILABLE; }
static void Stop() {}
#endif
ZenNativeMouseMonitor() = delete;
};
} // namespace zen
#endif

View File

@@ -0,0 +1,51 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ZenMouseTracker.h"
#include "ZenMouseTrackerInternal.h"
#include <windows.h>
namespace zen {
static HHOOK sMouseHook = nullptr;
// Runs on the thread that installed the hook (the main thread). Keep this as
// light as possible: the system silently removes hooks that take too long.
static LRESULT CALLBACK MouseHookProc(int aCode, WPARAM aWParam,
LPARAM aLParam) {
if (aCode == HC_ACTION && aWParam == WM_MOUSEMOVE) {
const auto* info = reinterpret_cast<MSLLHOOKSTRUCT*>(aLParam);
// Screen physical pixels, which are what desktop pixels map to on Windows
ZenMouseTracker::OnNativePointerMove(
mozilla::DesktopPoint(float(info->pt.x), float(info->pt.y)));
}
return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
}
nsresult ZenNativeMouseMonitor::Start() {
if (sMouseHook) {
return NS_OK;
}
// Pass the module that actually contains the hook proc (xul.dll), not the
// executable's module
HMODULE module = nullptr;
if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(&MouseHookProc),
&module)) {
return NS_ERROR_NOT_AVAILABLE;
}
sMouseHook = ::SetWindowsHookExW(WH_MOUSE_LL, MouseHookProc, module, 0);
return sMouseHook ? NS_OK : NS_ERROR_NOT_AVAILABLE;
}
void ZenNativeMouseMonitor::Stop() {
if (sMouseHook) {
::UnhookWindowsHookEx(sMouseHook);
sMouseHook = nullptr;
}
}
} // namespace zen

View File

@@ -0,0 +1,14 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
Classes = [
{
'cid': '{03d900f7-639f-4c81-b029-2883dd893ccb}',
'interfaces': ['nsIZenMouseTracker'],
'contract_ids': ['@mozilla.org/zen/mouse-tracker;1'],
'type': 'zen::ZenMouseTracker',
'headers': ['mozilla/ZenMouseTracker.h'],
'processes': ProcessSelector.MAIN_PROCESS_ONLY,
},
]

View File

@@ -0,0 +1,41 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
XPIDL_SOURCES += [
"nsIZenMouseTracker.idl",
]
EXPORTS.mozilla += [
"ZenMouseTracker.h",
"ZenMouseTrackerInternal.h",
]
SOURCES += [
"ZenMouseTracker.cpp",
]
XPCOM_MANIFESTS += [
"components.conf",
]
LOCAL_INCLUDES += [
"/widget",
]
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
SOURCES += ["ZenMouseTrackerCocoa.mm"]
LOCAL_INCLUDES += [
"/widget/cocoa",
"/xpcom/base",
]
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
SOURCES += ["ZenMouseTrackerWin.cpp"]
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
SOURCES += ["ZenMouseTrackerGtk.cpp"]
CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
FINAL_LIBRARY = "xul"
XPIDL_MODULE = "zen_compact_mode"

View File

@@ -0,0 +1,41 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface mozIDOMWindowProxy;
/**
* @brief Tracks the OS pointer for windows the pointer has left.
* Once the pointer either crosses the window bounds on the
* axis perpendicular to the registered edge, or moves further than the
* given offset past that edge, the "zen-mouse-tracker:exited" observer
* notification is fired with the window as subject and tracking for that
* window stops.
*/
[scriptable, uuid(023e85e6-44e1-46b3-9cfb-6f3c7fa075f8)]
interface nsIZenMouseTracker : nsISupports {
/**
* @brief Start tracking the pointer for the given window.
*
* Re-registering a window updates its edge and offset. Tracking is
* automatically cleared when the window is closed.
*
* @param window The window the pointer just left.
* @param screenEdge The edge the pointer left through, one of "left",
* "right", "top" or "bottom".
* @param maxEdgeOffset How far (in CSS pixels) the pointer may travel past
* screenEdge before the exit notification is fired.
* @throws NS_ERROR_NOT_AVAILABLE when the platform cannot track the global
* pointer position (e.g. on Wayland).
*/
void registerWindow(in mozIDOMWindowProxy window, in ACString screenEdge,
in float maxEdgeOffset);
/**
* @brief Stop tracking the pointer for the given window. No-op if the
* window isn't being tracked.
*/
void unregisterWindow(in mozIDOMWindowProxy window);
};

View File

@@ -9,6 +9,7 @@ EXTRA_PP_COMPONENTS += [
DIRS += [
"boosts",
"common",
"compact-mode",
"drag-and-drop",
"glance",
"live-folders",