| Index: ui/views/cocoa/cocoa_non_client_drag.mm
|
| diff --git a/ui/views/cocoa/cocoa_non_client_drag.mm b/ui/views/cocoa/cocoa_non_client_drag.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..917bdc65a72c819a649577eaf343da8e00f521ed
|
| --- /dev/null
|
| +++ b/ui/views/cocoa/cocoa_non_client_drag.mm
|
| @@ -0,0 +1,160 @@
|
| +// Copyright 2015 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#import "ui/views/cocoa/cocoa_non_client_drag.h"
|
| +
|
| +#import <Cocoa/Cocoa.h>
|
| +
|
| +#include "base/logging.h"
|
| +#import "base/mac/foundation_util.h"
|
| +#include "base/process/process_handle.h"
|
| +
|
| +@implementation CocoaNonClientDragMaskView
|
| +
|
| +- (BOOL)mouseDownCanMoveWindow {
|
| + return NO;
|
| +}
|
| +
|
| +- (NSView*)hitTest:(NSPoint)aPoint {
|
| + return nil;
|
| +}
|
| +
|
| +@end
|
| +
|
| +namespace views {
|
| +
|
| +namespace {
|
| +
|
| +int g_ref_count = 0;
|
| +CGEventRef g_last_reposted_event = 0;
|
| +
|
| +CFRunLoopSourceRef g_run_loop_source = 0;
|
| +CFMachPortRef g_mach_port = 0;
|
| +
|
| +id g_event_monitor = nil;
|
| +
|
| +bool RepostEventIfHandledByWindow(NSWindow* window,
|
| + NSPoint location,
|
| + CGEventRef event) {
|
| + if ([window respondsToSelector:@selector(willReceiveLeftMouseDown:)] &&
|
| + [base::mac::ObjCCast<NativeWidgetMacNSWindow>(window)
|
| + willReceiveLeftMouseDown:location]) {
|
| + if (g_last_reposted_event != event) {
|
| + g_last_reposted_event = event;
|
| + CGEventPost(kCGSessionEventTap, event); // Repost event.
|
| + } else {
|
| + LOG(INFO) << "reposting already reposted event " << event;
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +CGEventRef EventTapCallBack(CGEventTapProxy proxy,
|
| + CGEventType type,
|
| + CGEventRef cg_event,
|
| + void* refcon) {
|
| + LOG(INFO) << "EventTapCallBack " << cg_event;
|
| + // Disable CGEventTap mechanism.
|
| + return cg_event;
|
| +
|
| + pid_t target_pid =
|
| + CGEventGetIntegerValueField(cg_event, kCGEventTargetUnixProcessID);
|
| + if (base::GetCurrentProcId() != target_pid)
|
| + return cg_event;
|
| +
|
| + if (type != kCGEventLeftMouseDown)
|
| + return cg_event;
|
| +
|
| + NSPoint location = NSPointFromCGPoint(CGEventGetUnflippedLocation(cg_event));
|
| +
|
| + // Find the window this event lands on.
|
| + NSWindow* target = nil;
|
| + for (NSWindow* window in [NSApp windows]) {
|
| + if (NSPointInRect(location, [window frame])) {
|
| + target = window;
|
| + break;
|
| + }
|
| + }
|
| + if (!target)
|
| + return cg_event;
|
| +
|
| + if (RepostEventIfHandledByWindow(
|
| + target, [target convertScreenToBase:location], cg_event)) {
|
| + return nil;
|
| + }
|
| +
|
| + return cg_event;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +CocoaNonClientDrag::CocoaNonClientDrag() {
|
| + if (g_ref_count)
|
| + return;
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// CGEventTap setup.
|
| + if (!g_mach_port) {
|
| + // We cant use kCGAnnotatedSessionEventTap or CGEventTapCreateForPSN because
|
| + // they both happen after window movement happens.
|
| + g_mach_port = CGEventTapCreate(
|
| + kCGSessionEventTap, // After event has been annotated to flow to an
|
| + // application.
|
| + kCGHeadInsertEventTap,
|
| + kCGEventTapOptionDefault, // An active listener, can swallow events.
|
| + 1 << kCGEventLeftMouseDown, &EventTapCallBack, NULL);
|
| + }
|
| + if (!g_run_loop_source)
|
| + g_run_loop_source = CFMachPortCreateRunLoopSource(NULL, g_mach_port, 0);
|
| +
|
| + CFRunLoopAddSource(CFRunLoopGetMain(), g_run_loop_source,
|
| + kCFRunLoopCommonModes);
|
| + CGEventTapEnable(g_mach_port, true);
|
| +//
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// NSEvent local monitor setup.
|
| + NSEvent* (^monitor_callback)(NSEvent* ns_event);
|
| + monitor_callback = ^NSEvent*(NSEvent* ns_event) {
|
| + CGEventRef cg_event = [ns_event CGEvent];
|
| + LOG(INFO) << "MonitorCallback " << cg_event;
|
| + if (RepostEventIfHandledByWindow([ns_event window],
|
| + [ns_event locationInWindow],
|
| + cg_event)) {
|
| + return nil;
|
| + }
|
| + return ns_event;
|
| + };
|
| +
|
| + g_event_monitor =
|
| + [NSEvent addLocalMonitorForEventsMatchingMask:NSLeftMouseDownMask
|
| + handler:monitor_callback];
|
| +//
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +
|
| + ++g_ref_count;
|
| +}
|
| +
|
| +CocoaNonClientDrag::~CocoaNonClientDrag() {
|
| + --g_ref_count;
|
| + if (g_ref_count)
|
| + return;
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// CGEventTap cleanup.
|
| + CFRunLoopRemoveSource(CFRunLoopGetMain(), g_run_loop_source,
|
| + kCFRunLoopCommonModes);
|
| +//
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// NSEvent local monitor cleanup.
|
| + if (g_event_monitor) {
|
| + [NSEvent removeMonitor:g_event_monitor];
|
| + g_event_monitor = nil;
|
| + }
|
| +//
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +}
|
| +
|
| +} // namespace views
|
|
|