910e62b5创建于 1月15日历史提交
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ui/views/event_monitor_mac.h"

#import <Cocoa/Cocoa.h>

#include <memory>

#include "base/apple/owned_objc.h"
#include "base/check.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "ui/display/screen.h"
#include "ui/events/event.h"
#include "ui/events/event_observer.h"
#include "ui/events/event_utils.h"
#include "ui/views/cocoa/native_widget_mac_ns_window_host.h"
#include "ui/views/event_monitor_remote_cocoa.h"

namespace views {

namespace {
bool g_use_remote_cocoa_for_testing = false;
}

// static
std::unique_ptr<EventMonitor> EventMonitor::CreateApplicationMonitor(
    ui::EventObserver* event_observer,
    gfx::NativeWindow context,
    const std::set<ui::EventType>& types) {
  // |context| is not needed on Mac.
  return std::make_unique<EventMonitorMac>(event_observer, gfx::NativeWindow(),
                                           types);
}

// static
std::unique_ptr<EventMonitor> EventMonitor::CreateWindowMonitor(
    ui::EventObserver* event_observer,
    gfx::NativeWindow target_window,
    const std::set<ui::EventType>& types) {
  CHECK(target_window)
      << "Use CreateApplicationMonitor to observe events to all windows";

  // For Progressive Web App (PWA) windows, we can't use an in-process NSEvent
  // monitors, as these windows exist in a different process. So depending on
  // if the target window is local or remote we use a different EventMonitor
  // implementation.
  auto* host =
      views::NativeWidgetMacNSWindowHost::GetFromNativeWindow(target_window);
  if (host && (host->application_host() || g_use_remote_cocoa_for_testing)) {
    return std::make_unique<EventMonitorRemoteCocoa>(event_observer,
                                                     target_window, types);
  } else {
    return std::make_unique<EventMonitorMac>(event_observer, target_window,
                                             types);
  }
}

struct EventMonitorMac::ObjCStorage {
  id __strong monitor = nil;
};

EventMonitorMac::EventMonitorMac(ui::EventObserver* event_observer,
                                 gfx::NativeWindow target_native_window,
                                 const std::set<ui::EventType>& types)
    : types_(types),
      event_observer_(event_observer),
      objc_storage_(std::make_unique<ObjCStorage>()) {
  DCHECK(event_observer);
  NSWindow* target_window = target_native_window.GetNativeNSWindow();

  // Capture a WeakPtr. This allows the block to detect another event monitor
  // for the same event deleting |this|.
  base::WeakPtr<EventMonitorMac> weak_ptr = factory_.GetWeakPtr();

  auto block = ^NSEvent*(NSEvent* event) {
    if (!weak_ptr) {
      return event;
    }

    if (!target_window || [event window] == target_window) {
      std::unique_ptr<ui::Event> ui_event =
          ui::EventFromNative(base::apple::OwnedNSEvent(event));
      if (ui_event && types_.find(ui_event->type()) != types_.end()) {
        event_observer->OnEvent(*ui_event);
      }
    }
    return event;
  };

  objc_storage_->monitor =
      [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskAny
                                            handler:block];
}

EventMonitorMac::~EventMonitorMac() {
  [NSEvent removeMonitor:objc_storage_->monitor];
}

gfx::Point EventMonitorMac::GetLastMouseLocation() {
  return display::Screen::Get()->GetCursorScreenPoint();
}

// static
base::AutoReset<bool> EventMonitorMac::UseRemoteCocoaForTesting() {
  base::AutoReset<bool> result(&g_use_remote_cocoa_for_testing, true);
  return result;
}

}  // namespace views