skia2/tools/sk_app/mac/Window_mac.mm
Ben Wagner f6cc85844f Reland "Fix sk_app macOS raster window build conditions."
The "raster" window on macOS and iOS is actually backed by GL. Fix the
build rules and code conditions to reflect this. This allows for some
sk_app applications to run on macOS and iOS with skia_use_gl=false.

> Revert:
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/397737
> Commit-Queue: Brian Osman <brianosman@google.com>
> Reviewed-by: Brian Osman <brianosman@google.com>

> Land:
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/397256
> Commit-Queue: Ben Wagner <bungeman@google.com>
> Reviewed-by: Brian Osman <brianosman@google.com>

Change-Id: Ia8a421f4818856dd90cb4847095eee0d1836d1e6
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/398056
Reviewed-by: Ben Wagner <bungeman@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
2021-04-19 18:25:44 +00:00

457 lines
14 KiB
Plaintext

/*
* Copyright 2019 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <Carbon/Carbon.h>
#include "src/core/SkUtils.h"
#include "tools/sk_app/mac/WindowContextFactory_mac.h"
#include "tools/sk_app/mac/Window_mac.h"
#include "tools/skui/ModifierKey.h"
@interface WindowDelegate : NSObject<NSWindowDelegate>
- (WindowDelegate*)initWithWindow:(sk_app::Window_mac*)initWindow;
@end
@interface MainView : NSView
- (MainView*)initWithWindow:(sk_app::Window_mac*)initWindow;
@end
///////////////////////////////////////////////////////////////////////////////
using sk_app::Window;
namespace sk_app {
SkTDynamicHash<Window_mac, NSInteger> Window_mac::gWindowMap;
Window* Window::CreateNativeWindow(void*) {
Window_mac* window = new Window_mac();
if (!window->initWindow()) {
delete window;
return nullptr;
}
return window;
}
bool Window_mac::initWindow() {
// we already have a window
if (fWindow) {
return true;
}
// Create a delegate to track certain events
WindowDelegate* delegate = [[WindowDelegate alloc] initWithWindow:this];
if (nil == delegate) {
return false;
}
// Create Cocoa window
constexpr int initialWidth = 1280;
constexpr int initialHeight = 960;
NSRect windowRect = NSMakeRect(100, 100, initialWidth, initialHeight);
NSUInteger windowStyle = (NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask |
NSMiniaturizableWindowMask);
fWindow = [[NSWindow alloc] initWithContentRect:windowRect styleMask:windowStyle
backing:NSBackingStoreBuffered defer:NO];
if (nil == fWindow) {
[delegate release];
return false;
}
// create view
MainView* view = [[MainView alloc] initWithWindow:this];
if (nil == view) {
[fWindow release];
[delegate release];
return false;
}
[fWindow setContentView:view];
[fWindow makeFirstResponder:view];
[fWindow setDelegate:delegate];
[fWindow setAcceptsMouseMovedEvents:YES];
[fWindow setRestorable:NO];
// Should be retained by window now
[view release];
fWindowNumber = fWindow.windowNumber;
gWindowMap.add(this);
return true;
}
void Window_mac::closeWindow() {
if (nil != fWindow) {
gWindowMap.remove(fWindowNumber);
if (sk_app::Window_mac::gWindowMap.count() < 1) {
[NSApp terminate:fWindow];
}
[fWindow close];
fWindow = nil;
}
}
void Window_mac::setTitle(const char* title) {
if (NSString* titleStr = [NSString stringWithUTF8String:title]) {
[fWindow setTitle:titleStr];
}
}
void Window_mac::show() {
[fWindow orderFront:nil];
[NSApp activateIgnoringOtherApps:YES];
[fWindow makeKeyAndOrderFront:NSApp];
}
bool Window_mac::attach(BackendType attachType) {
this->initWindow();
window_context_factory::MacWindowInfo info;
info.fMainView = [fWindow contentView];
switch (attachType) {
#ifdef SK_DAWN
case kDawn_BackendType:
fWindowContext = MakeDawnMTLForMac(info, fRequestedDisplayParams);
break;
#endif
#ifdef SK_VULKAN
case kVulkan_BackendType:
fWindowContext = MakeVulkanForMac(info, fRequestedDisplayParams);
break;
#endif
#ifdef SK_METAL
case kMetal_BackendType:
fWindowContext = MakeMetalForMac(info, fRequestedDisplayParams);
break;
#endif
#ifdef SK_GL
case kNativeGL_BackendType:
fWindowContext = MakeGLForMac(info, fRequestedDisplayParams);
break;
case kRaster_BackendType:
fWindowContext = MakeRasterForMac(info, fRequestedDisplayParams);
break;
#endif
default:
SkASSERT_RELEASE(false);
}
this->onBackendCreated();
return SkToBool(fWindowContext);
}
float Window_mac::scaleFactor() const {
return sk_app::GetBackingScaleFactor(fWindow.contentView);
}
void Window_mac::PaintWindows() {
gWindowMap.foreach([&](Window_mac* window) {
if (window->fIsContentInvalidated) {
window->onPaint();
}
});
}
} // namespace sk_app
///////////////////////////////////////////////////////////////////////////////
@implementation WindowDelegate {
sk_app::Window_mac* fWindow;
}
- (WindowDelegate*)initWithWindow:(sk_app::Window_mac *)initWindow {
fWindow = initWindow;
return self;
}
- (void)windowDidResize:(NSNotification *)notification {
NSView* view = fWindow->window().contentView;
CGFloat scale = sk_app::GetBackingScaleFactor(view);
fWindow->onResize(view.bounds.size.width * scale, view.bounds.size.height * scale);
fWindow->inval();
}
- (BOOL)windowShouldClose:(NSWindow*)sender {
fWindow->closeWindow();
return FALSE;
}
@end
///////////////////////////////////////////////////////////////////////////////
static skui::Key get_key(unsigned short vk) {
// This will work with an ANSI QWERTY keyboard.
// Something more robust would be needed to support alternate keyboards.
static const struct {
unsigned short fVK;
skui::Key fKey;
} gPair[] = {
{ kVK_Delete, skui::Key::kBack },
{ kVK_Return, skui::Key::kOK },
{ kVK_UpArrow, skui::Key::kUp },
{ kVK_DownArrow, skui::Key::kDown },
{ kVK_LeftArrow, skui::Key::kLeft },
{ kVK_RightArrow, skui::Key::kRight },
{ kVK_Tab, skui::Key::kTab },
{ kVK_PageUp, skui::Key::kPageUp },
{ kVK_PageDown, skui::Key::kPageDown },
{ kVK_Home, skui::Key::kHome },
{ kVK_End, skui::Key::kEnd },
{ kVK_ForwardDelete, skui::Key::kDelete },
{ kVK_Escape, skui::Key::kEscape },
{ kVK_Shift, skui::Key::kShift },
{ kVK_RightShift, skui::Key::kShift },
{ kVK_Control, skui::Key::kCtrl },
{ kVK_RightControl, skui::Key::kCtrl },
{ kVK_Option, skui::Key::kOption },
{ kVK_RightOption, skui::Key::kOption },
{ kVK_Command, skui::Key::kSuper },
{ kVK_RightCommand, skui::Key::kSuper },
{ kVK_ANSI_A, skui::Key::kA },
{ kVK_ANSI_C, skui::Key::kC },
{ kVK_ANSI_V, skui::Key::kV },
{ kVK_ANSI_X, skui::Key::kX },
{ kVK_ANSI_Y, skui::Key::kY },
{ kVK_ANSI_Z, skui::Key::kZ },
};
for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) {
if (gPair[i].fVK == vk) {
return gPair[i].fKey;
}
}
return skui::Key::kNONE;
}
static skui::ModifierKey get_modifiers(const NSEvent* event) {
NSUInteger modifierFlags = [event modifierFlags];
skui::ModifierKey modifiers = skui::ModifierKey::kNone;
if (modifierFlags & NSEventModifierFlagCommand) {
modifiers |= skui::ModifierKey::kCommand;
}
if (modifierFlags & NSEventModifierFlagShift) {
modifiers |= skui::ModifierKey::kShift;
}
if (modifierFlags & NSEventModifierFlagControl) {
modifiers |= skui::ModifierKey::kControl;
}
if (modifierFlags & NSEventModifierFlagOption) {
modifiers |= skui::ModifierKey::kOption;
}
if ((NSKeyDown == [event type] || NSKeyUp == [event type]) && ![event isARepeat]) {
modifiers |= skui::ModifierKey::kFirstPress;
}
return modifiers;
}
@implementation MainView {
sk_app::Window_mac* fWindow;
// A TrackingArea prevents us from capturing events outside the view
NSTrackingArea* fTrackingArea;
// We keep track of the state of the modifier keys on each event in order to synthesize
// key-up/down events for each modifier.
skui::ModifierKey fLastModifiers;
}
- (MainView*)initWithWindow:(sk_app::Window_mac *)initWindow {
self = [super init];
fWindow = initWindow;
fTrackingArea = nil;
[self updateTrackingAreas];
return self;
}
- (void)dealloc
{
[fTrackingArea release];
[super dealloc];
}
- (BOOL)isOpaque {
return YES;
}
- (BOOL)canBecomeKeyView {
return YES;
}
- (BOOL)acceptsFirstResponder {
return YES;
}
- (void)updateTrackingAreas {
if (fTrackingArea != nil) {
[self removeTrackingArea:fTrackingArea];
[fTrackingArea release];
}
const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited |
NSTrackingActiveInKeyWindow |
NSTrackingEnabledDuringMouseDrag |
NSTrackingCursorUpdate |
NSTrackingInVisibleRect |
NSTrackingAssumeInside;
fTrackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
options:options
owner:self
userInfo:nil];
[self addTrackingArea:fTrackingArea];
[super updateTrackingAreas];
}
- (skui::ModifierKey) updateModifierKeys:(NSEvent*) event {
using sknonstd::Any;
skui::ModifierKey modifiers = get_modifiers(event);
skui::ModifierKey changed = modifiers ^ fLastModifiers;
fLastModifiers = modifiers;
struct ModMap {
skui::ModifierKey modifier;
skui::Key key;
};
// Map each modifier bit to the equivalent skui Key and send key-up/down events.
for (const ModMap& cur : {ModMap{skui::ModifierKey::kCommand, skui::Key::kSuper},
ModMap{skui::ModifierKey::kShift, skui::Key::kShift},
ModMap{skui::ModifierKey::kControl, skui::Key::kCtrl},
ModMap{skui::ModifierKey::kOption, skui::Key::kOption}}) {
if (Any(changed & cur.modifier)) {
const skui::InputState state = Any(modifiers & cur.modifier) ? skui::InputState::kDown
: skui::InputState::kUp;
(void) fWindow->onKey(cur.key, state, modifiers);
}
}
return modifiers;
}
- (BOOL)performKeyEquivalent:(NSEvent *)event {
[self updateModifierKeys:event];
// By default, unhandled key equivalents send -keyDown events; unfortunately, they do not send
// a matching -keyUp. In other words, we can claim that we didn't handle the event and OS X will
// turn this event into a -keyDown automatically, but we need to synthesize a matching -keyUp on
// a later frame. Since we only read the modifiers and key code from the event, we can reuse
// this "key-equivalent" event as a "key up".
[self performSelector:@selector(keyUp:) withObject:event afterDelay:0.1];
return NO;
}
- (void)keyDown:(NSEvent *)event {
skui::ModifierKey modifiers = [self updateModifierKeys:event];
skui::Key key = get_key([event keyCode]);
if (key != skui::Key::kNONE) {
if (!fWindow->onKey(key, skui::InputState::kDown, modifiers)) {
if (skui::Key::kEscape == key) {
[NSApp terminate:fWindow->window()];
}
}
}
NSString* characters = [event charactersIgnoringModifiers];
NSUInteger len = [characters length];
if (len > 0) {
unichar* charBuffer = new unichar[len+1];
[characters getCharacters:charBuffer range:NSMakeRange(0, len)];
for (NSUInteger i = 0; i < len; ++i) {
(void) fWindow->onChar((SkUnichar) charBuffer[i], modifiers);
}
delete [] charBuffer;
}
}
- (void)keyUp:(NSEvent *)event {
skui::ModifierKey modifiers = [self updateModifierKeys:event];
skui::Key key = get_key([event keyCode]);
if (key != skui::Key::kNONE) {
(void) fWindow->onKey(key, skui::InputState::kUp, modifiers);
}
}
-(void)flagsChanged:(NSEvent *)event {
[self updateModifierKeys:event];
}
- (void)mouseDown:(NSEvent *)event {
NSView* view = fWindow->window().contentView;
CGFloat backingScaleFactor = sk_app::GetBackingScaleFactor(view);
skui::ModifierKey modifiers = [self updateModifierKeys:event];
const NSPoint pos = [event locationInWindow];
const NSRect rect = [view frame];
fWindow->onMouse(pos.x * backingScaleFactor, (rect.size.height - pos.y) * backingScaleFactor,
skui::InputState::kDown, modifiers);
}
- (void)mouseUp:(NSEvent *)event {
NSView* view = fWindow->window().contentView;
CGFloat backingScaleFactor = sk_app::GetBackingScaleFactor(view);
skui::ModifierKey modifiers = [self updateModifierKeys:event];
const NSPoint pos = [event locationInWindow];
const NSRect rect = [view frame];
fWindow->onMouse(pos.x * backingScaleFactor, (rect.size.height - pos.y) * backingScaleFactor,
skui::InputState::kUp, modifiers);
}
- (void)mouseDragged:(NSEvent *)event {
[self updateModifierKeys:event];
[self mouseMoved:event];
}
- (void)mouseMoved:(NSEvent *)event {
NSView* view = fWindow->window().contentView;
CGFloat backingScaleFactor = sk_app::GetBackingScaleFactor(view);
skui::ModifierKey modifiers = [self updateModifierKeys:event];
const NSPoint pos = [event locationInWindow];
const NSRect rect = [view frame];
fWindow->onMouse(pos.x * backingScaleFactor, (rect.size.height - pos.y) * backingScaleFactor,
skui::InputState::kMove, modifiers);
}
- (void)scrollWheel:(NSEvent *)event {
skui::ModifierKey modifiers = [self updateModifierKeys:event];
// TODO: support hasPreciseScrollingDeltas?
fWindow->onMouseWheel([event scrollingDeltaY], modifiers);
}
- (void)drawRect:(NSRect)rect {
fWindow->onPaint();
}
@end