1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci* Copyright 2019 Google Inc.
3cb93a386Sopenharmony_ci*
4cb93a386Sopenharmony_ci* Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci* found in the LICENSE file.
6cb93a386Sopenharmony_ci*/
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include <Carbon/Carbon.h>
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "include/core/SkTypes.h"
11cb93a386Sopenharmony_ci#include "tools/sk_app/mac/WindowContextFactory_mac.h"
12cb93a386Sopenharmony_ci#include "tools/sk_app/mac/Window_mac.h"
13cb93a386Sopenharmony_ci#include "tools/skui/ModifierKey.h"
14cb93a386Sopenharmony_ci
15cb93a386Sopenharmony_ci@interface WindowDelegate : NSObject<NSWindowDelegate>
16cb93a386Sopenharmony_ci
17cb93a386Sopenharmony_ci- (WindowDelegate*)initWithWindow:(sk_app::Window_mac*)initWindow;
18cb93a386Sopenharmony_ci
19cb93a386Sopenharmony_ci@end
20cb93a386Sopenharmony_ci
21cb93a386Sopenharmony_ci@interface MainView : NSView
22cb93a386Sopenharmony_ci
23cb93a386Sopenharmony_ci- (MainView*)initWithWindow:(sk_app::Window_mac*)initWindow;
24cb93a386Sopenharmony_ci
25cb93a386Sopenharmony_ci@end
26cb93a386Sopenharmony_ci
27cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
28cb93a386Sopenharmony_ci
29cb93a386Sopenharmony_ciusing sk_app::Window;
30cb93a386Sopenharmony_ci
31cb93a386Sopenharmony_cinamespace sk_app {
32cb93a386Sopenharmony_ci
33cb93a386Sopenharmony_ciSkTDynamicHash<Window_mac, NSInteger> Window_mac::gWindowMap;
34cb93a386Sopenharmony_ci
35cb93a386Sopenharmony_ciWindow* Window::CreateNativeWindow(void*) {
36cb93a386Sopenharmony_ci    Window_mac* window = new Window_mac();
37cb93a386Sopenharmony_ci    if (!window->initWindow()) {
38cb93a386Sopenharmony_ci        delete window;
39cb93a386Sopenharmony_ci        return nullptr;
40cb93a386Sopenharmony_ci    }
41cb93a386Sopenharmony_ci
42cb93a386Sopenharmony_ci    return window;
43cb93a386Sopenharmony_ci}
44cb93a386Sopenharmony_ci
45cb93a386Sopenharmony_cibool Window_mac::initWindow() {
46cb93a386Sopenharmony_ci    // we already have a window
47cb93a386Sopenharmony_ci    if (fWindow) {
48cb93a386Sopenharmony_ci        return true;
49cb93a386Sopenharmony_ci    }
50cb93a386Sopenharmony_ci
51cb93a386Sopenharmony_ci    // Create a delegate to track certain events
52cb93a386Sopenharmony_ci    WindowDelegate* delegate = [[WindowDelegate alloc] initWithWindow:this];
53cb93a386Sopenharmony_ci    if (nil == delegate) {
54cb93a386Sopenharmony_ci        return false;
55cb93a386Sopenharmony_ci    }
56cb93a386Sopenharmony_ci
57cb93a386Sopenharmony_ci    // Create Cocoa window
58cb93a386Sopenharmony_ci    constexpr int initialWidth = 1280;
59cb93a386Sopenharmony_ci    constexpr int initialHeight = 960;
60cb93a386Sopenharmony_ci    NSRect windowRect = NSMakeRect(100, 100, initialWidth, initialHeight);
61cb93a386Sopenharmony_ci
62cb93a386Sopenharmony_ci    NSUInteger windowStyle = (NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask |
63cb93a386Sopenharmony_ci                              NSMiniaturizableWindowMask);
64cb93a386Sopenharmony_ci
65cb93a386Sopenharmony_ci    fWindow = [[NSWindow alloc] initWithContentRect:windowRect styleMask:windowStyle
66cb93a386Sopenharmony_ci                                backing:NSBackingStoreBuffered defer:NO];
67cb93a386Sopenharmony_ci    if (nil == fWindow) {
68cb93a386Sopenharmony_ci        [delegate release];
69cb93a386Sopenharmony_ci        return false;
70cb93a386Sopenharmony_ci    }
71cb93a386Sopenharmony_ci
72cb93a386Sopenharmony_ci    // create view
73cb93a386Sopenharmony_ci    MainView* view = [[MainView alloc] initWithWindow:this];
74cb93a386Sopenharmony_ci    if (nil == view) {
75cb93a386Sopenharmony_ci        [fWindow release];
76cb93a386Sopenharmony_ci        [delegate release];
77cb93a386Sopenharmony_ci        return false;
78cb93a386Sopenharmony_ci    }
79cb93a386Sopenharmony_ci
80cb93a386Sopenharmony_ci    [fWindow setContentView:view];
81cb93a386Sopenharmony_ci    [fWindow makeFirstResponder:view];
82cb93a386Sopenharmony_ci    [fWindow setDelegate:delegate];
83cb93a386Sopenharmony_ci    [fWindow setAcceptsMouseMovedEvents:YES];
84cb93a386Sopenharmony_ci    [fWindow setRestorable:NO];
85cb93a386Sopenharmony_ci
86cb93a386Sopenharmony_ci    // Should be retained by window now
87cb93a386Sopenharmony_ci    [view release];
88cb93a386Sopenharmony_ci
89cb93a386Sopenharmony_ci    fWindowNumber = fWindow.windowNumber;
90cb93a386Sopenharmony_ci    gWindowMap.add(this);
91cb93a386Sopenharmony_ci
92cb93a386Sopenharmony_ci    return true;
93cb93a386Sopenharmony_ci}
94cb93a386Sopenharmony_ci
95cb93a386Sopenharmony_civoid Window_mac::closeWindow() {
96cb93a386Sopenharmony_ci    if (nil != fWindow) {
97cb93a386Sopenharmony_ci        gWindowMap.remove(fWindowNumber);
98cb93a386Sopenharmony_ci        if (sk_app::Window_mac::gWindowMap.count() < 1) {
99cb93a386Sopenharmony_ci            [NSApp terminate:fWindow];
100cb93a386Sopenharmony_ci        }
101cb93a386Sopenharmony_ci        [fWindow close];
102cb93a386Sopenharmony_ci        fWindow = nil;
103cb93a386Sopenharmony_ci    }
104cb93a386Sopenharmony_ci}
105cb93a386Sopenharmony_ci
106cb93a386Sopenharmony_civoid Window_mac::setTitle(const char* title) {
107cb93a386Sopenharmony_ci    if (NSString* titleStr = [NSString stringWithUTF8String:title]) {
108cb93a386Sopenharmony_ci        [fWindow setTitle:titleStr];
109cb93a386Sopenharmony_ci    }
110cb93a386Sopenharmony_ci}
111cb93a386Sopenharmony_ci
112cb93a386Sopenharmony_civoid Window_mac::show() {
113cb93a386Sopenharmony_ci    [fWindow orderFront:nil];
114cb93a386Sopenharmony_ci
115cb93a386Sopenharmony_ci    [NSApp activateIgnoringOtherApps:YES];
116cb93a386Sopenharmony_ci    [fWindow makeKeyAndOrderFront:NSApp];
117cb93a386Sopenharmony_ci}
118cb93a386Sopenharmony_ci
119cb93a386Sopenharmony_cibool Window_mac::attach(BackendType attachType) {
120cb93a386Sopenharmony_ci    this->initWindow();
121cb93a386Sopenharmony_ci
122cb93a386Sopenharmony_ci    window_context_factory::MacWindowInfo info;
123cb93a386Sopenharmony_ci    info.fMainView = [fWindow contentView];
124cb93a386Sopenharmony_ci    switch (attachType) {
125cb93a386Sopenharmony_ci#ifdef SK_DAWN
126cb93a386Sopenharmony_ci        case kDawn_BackendType:
127cb93a386Sopenharmony_ci            fWindowContext = MakeDawnMTLForMac(info, fRequestedDisplayParams);
128cb93a386Sopenharmony_ci            break;
129cb93a386Sopenharmony_ci#endif
130cb93a386Sopenharmony_ci#ifdef SK_VULKAN
131cb93a386Sopenharmony_ci        case kVulkan_BackendType:
132cb93a386Sopenharmony_ci            fWindowContext = MakeVulkanForMac(info, fRequestedDisplayParams);
133cb93a386Sopenharmony_ci            break;
134cb93a386Sopenharmony_ci#endif
135cb93a386Sopenharmony_ci#ifdef SK_METAL
136cb93a386Sopenharmony_ci        case kMetal_BackendType:
137cb93a386Sopenharmony_ci            fWindowContext = MakeMetalForMac(info, fRequestedDisplayParams);
138cb93a386Sopenharmony_ci            break;
139cb93a386Sopenharmony_ci#ifdef SK_GRAPHITE_ENABLED
140cb93a386Sopenharmony_ci        case kGraphiteMetal_BackendType:
141cb93a386Sopenharmony_ci            fWindowContext = MakeGraphiteMetalForMac(info, fRequestedDisplayParams);
142cb93a386Sopenharmony_ci            break;
143cb93a386Sopenharmony_ci#endif
144cb93a386Sopenharmony_ci#endif
145cb93a386Sopenharmony_ci#ifdef SK_GL
146cb93a386Sopenharmony_ci        case kNativeGL_BackendType:
147cb93a386Sopenharmony_ci            fWindowContext = MakeGLForMac(info, fRequestedDisplayParams);
148cb93a386Sopenharmony_ci            break;
149cb93a386Sopenharmony_ci        case kRaster_BackendType:
150cb93a386Sopenharmony_ci            fWindowContext = MakeRasterForMac(info, fRequestedDisplayParams);
151cb93a386Sopenharmony_ci            break;
152cb93a386Sopenharmony_ci#endif
153cb93a386Sopenharmony_ci        default:
154cb93a386Sopenharmony_ci            SkASSERT_RELEASE(false);
155cb93a386Sopenharmony_ci    }
156cb93a386Sopenharmony_ci    this->onBackendCreated();
157cb93a386Sopenharmony_ci
158cb93a386Sopenharmony_ci    return SkToBool(fWindowContext);
159cb93a386Sopenharmony_ci}
160cb93a386Sopenharmony_ci
161cb93a386Sopenharmony_cifloat Window_mac::scaleFactor() const {
162cb93a386Sopenharmony_ci    return sk_app::GetBackingScaleFactor(fWindow.contentView);
163cb93a386Sopenharmony_ci}
164cb93a386Sopenharmony_ci
165cb93a386Sopenharmony_civoid Window_mac::PaintWindows() {
166cb93a386Sopenharmony_ci    gWindowMap.foreach([&](Window_mac* window) {
167cb93a386Sopenharmony_ci        if (window->fIsContentInvalidated) {
168cb93a386Sopenharmony_ci            window->onPaint();
169cb93a386Sopenharmony_ci        }
170cb93a386Sopenharmony_ci    });
171cb93a386Sopenharmony_ci}
172cb93a386Sopenharmony_ci
173cb93a386Sopenharmony_ci}   // namespace sk_app
174cb93a386Sopenharmony_ci
175cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
176cb93a386Sopenharmony_ci
177cb93a386Sopenharmony_ci@implementation WindowDelegate {
178cb93a386Sopenharmony_ci    sk_app::Window_mac* fWindow;
179cb93a386Sopenharmony_ci}
180cb93a386Sopenharmony_ci
181cb93a386Sopenharmony_ci- (WindowDelegate*)initWithWindow:(sk_app::Window_mac *)initWindow {
182cb93a386Sopenharmony_ci    fWindow = initWindow;
183cb93a386Sopenharmony_ci
184cb93a386Sopenharmony_ci    return self;
185cb93a386Sopenharmony_ci}
186cb93a386Sopenharmony_ci
187cb93a386Sopenharmony_ci- (void)windowDidResize:(NSNotification *)notification {
188cb93a386Sopenharmony_ci    NSView* view = fWindow->window().contentView;
189cb93a386Sopenharmony_ci    CGFloat scale = sk_app::GetBackingScaleFactor(view);
190cb93a386Sopenharmony_ci    fWindow->onResize(view.bounds.size.width * scale, view.bounds.size.height * scale);
191cb93a386Sopenharmony_ci    fWindow->inval();
192cb93a386Sopenharmony_ci}
193cb93a386Sopenharmony_ci
194cb93a386Sopenharmony_ci- (BOOL)windowShouldClose:(NSWindow*)sender {
195cb93a386Sopenharmony_ci    fWindow->closeWindow();
196cb93a386Sopenharmony_ci
197cb93a386Sopenharmony_ci    return FALSE;
198cb93a386Sopenharmony_ci}
199cb93a386Sopenharmony_ci
200cb93a386Sopenharmony_ci@end
201cb93a386Sopenharmony_ci
202cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
203cb93a386Sopenharmony_ci
204cb93a386Sopenharmony_cistatic skui::Key get_key(unsigned short vk) {
205cb93a386Sopenharmony_ci    // This will work with an ANSI QWERTY keyboard.
206cb93a386Sopenharmony_ci    // Something more robust would be needed to support alternate keyboards.
207cb93a386Sopenharmony_ci    static const struct {
208cb93a386Sopenharmony_ci        unsigned short fVK;
209cb93a386Sopenharmony_ci        skui::Key      fKey;
210cb93a386Sopenharmony_ci    } gPair[] = {
211cb93a386Sopenharmony_ci        { kVK_Delete,        skui::Key::kBack },
212cb93a386Sopenharmony_ci        { kVK_Return,        skui::Key::kOK },
213cb93a386Sopenharmony_ci        { kVK_UpArrow,       skui::Key::kUp },
214cb93a386Sopenharmony_ci        { kVK_DownArrow,     skui::Key::kDown },
215cb93a386Sopenharmony_ci        { kVK_LeftArrow,     skui::Key::kLeft },
216cb93a386Sopenharmony_ci        { kVK_RightArrow,    skui::Key::kRight },
217cb93a386Sopenharmony_ci        { kVK_Tab,           skui::Key::kTab },
218cb93a386Sopenharmony_ci        { kVK_PageUp,        skui::Key::kPageUp },
219cb93a386Sopenharmony_ci        { kVK_PageDown,      skui::Key::kPageDown },
220cb93a386Sopenharmony_ci        { kVK_Home,          skui::Key::kHome },
221cb93a386Sopenharmony_ci        { kVK_End,           skui::Key::kEnd },
222cb93a386Sopenharmony_ci        { kVK_ForwardDelete, skui::Key::kDelete },
223cb93a386Sopenharmony_ci        { kVK_Escape,        skui::Key::kEscape },
224cb93a386Sopenharmony_ci        { kVK_Shift,         skui::Key::kShift },
225cb93a386Sopenharmony_ci        { kVK_RightShift,    skui::Key::kShift },
226cb93a386Sopenharmony_ci        { kVK_Control,       skui::Key::kCtrl },
227cb93a386Sopenharmony_ci        { kVK_RightControl,  skui::Key::kCtrl },
228cb93a386Sopenharmony_ci        { kVK_Option,        skui::Key::kOption },
229cb93a386Sopenharmony_ci        { kVK_RightOption,   skui::Key::kOption },
230cb93a386Sopenharmony_ci        { kVK_Command,       skui::Key::kSuper },
231cb93a386Sopenharmony_ci        { kVK_RightCommand,  skui::Key::kSuper },
232cb93a386Sopenharmony_ci        { kVK_ANSI_A,        skui::Key::kA },
233cb93a386Sopenharmony_ci        { kVK_ANSI_C,        skui::Key::kC },
234cb93a386Sopenharmony_ci        { kVK_ANSI_V,        skui::Key::kV },
235cb93a386Sopenharmony_ci        { kVK_ANSI_X,        skui::Key::kX },
236cb93a386Sopenharmony_ci        { kVK_ANSI_Y,        skui::Key::kY },
237cb93a386Sopenharmony_ci        { kVK_ANSI_Z,        skui::Key::kZ },
238cb93a386Sopenharmony_ci    };
239cb93a386Sopenharmony_ci
240cb93a386Sopenharmony_ci    for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) {
241cb93a386Sopenharmony_ci        if (gPair[i].fVK == vk) {
242cb93a386Sopenharmony_ci            return gPair[i].fKey;
243cb93a386Sopenharmony_ci        }
244cb93a386Sopenharmony_ci    }
245cb93a386Sopenharmony_ci
246cb93a386Sopenharmony_ci    return skui::Key::kNONE;
247cb93a386Sopenharmony_ci}
248cb93a386Sopenharmony_ci
249cb93a386Sopenharmony_cistatic skui::ModifierKey get_modifiers(const NSEvent* event) {
250cb93a386Sopenharmony_ci    NSUInteger modifierFlags = [event modifierFlags];
251cb93a386Sopenharmony_ci    skui::ModifierKey modifiers = skui::ModifierKey::kNone;
252cb93a386Sopenharmony_ci
253cb93a386Sopenharmony_ci    if (modifierFlags & NSEventModifierFlagCommand) {
254cb93a386Sopenharmony_ci        modifiers |= skui::ModifierKey::kCommand;
255cb93a386Sopenharmony_ci    }
256cb93a386Sopenharmony_ci    if (modifierFlags & NSEventModifierFlagShift) {
257cb93a386Sopenharmony_ci        modifiers |= skui::ModifierKey::kShift;
258cb93a386Sopenharmony_ci    }
259cb93a386Sopenharmony_ci    if (modifierFlags & NSEventModifierFlagControl) {
260cb93a386Sopenharmony_ci        modifiers |= skui::ModifierKey::kControl;
261cb93a386Sopenharmony_ci    }
262cb93a386Sopenharmony_ci    if (modifierFlags & NSEventModifierFlagOption) {
263cb93a386Sopenharmony_ci        modifiers |= skui::ModifierKey::kOption;
264cb93a386Sopenharmony_ci    }
265cb93a386Sopenharmony_ci
266cb93a386Sopenharmony_ci    if ((NSKeyDown == [event type] || NSKeyUp == [event type]) && ![event isARepeat]) {
267cb93a386Sopenharmony_ci        modifiers |= skui::ModifierKey::kFirstPress;
268cb93a386Sopenharmony_ci    }
269cb93a386Sopenharmony_ci
270cb93a386Sopenharmony_ci    return modifiers;
271cb93a386Sopenharmony_ci}
272cb93a386Sopenharmony_ci
273cb93a386Sopenharmony_ci@implementation MainView {
274cb93a386Sopenharmony_ci    sk_app::Window_mac* fWindow;
275cb93a386Sopenharmony_ci    // A TrackingArea prevents us from capturing events outside the view
276cb93a386Sopenharmony_ci    NSTrackingArea* fTrackingArea;
277cb93a386Sopenharmony_ci    // We keep track of the state of the modifier keys on each event in order to synthesize
278cb93a386Sopenharmony_ci    // key-up/down events for each modifier.
279cb93a386Sopenharmony_ci    skui::ModifierKey fLastModifiers;
280cb93a386Sopenharmony_ci}
281cb93a386Sopenharmony_ci
282cb93a386Sopenharmony_ci- (MainView*)initWithWindow:(sk_app::Window_mac *)initWindow {
283cb93a386Sopenharmony_ci    self = [super init];
284cb93a386Sopenharmony_ci
285cb93a386Sopenharmony_ci    fWindow = initWindow;
286cb93a386Sopenharmony_ci    fTrackingArea = nil;
287cb93a386Sopenharmony_ci
288cb93a386Sopenharmony_ci    [self updateTrackingAreas];
289cb93a386Sopenharmony_ci
290cb93a386Sopenharmony_ci    return self;
291cb93a386Sopenharmony_ci}
292cb93a386Sopenharmony_ci
293cb93a386Sopenharmony_ci- (void)dealloc
294cb93a386Sopenharmony_ci{
295cb93a386Sopenharmony_ci    [fTrackingArea release];
296cb93a386Sopenharmony_ci    [super dealloc];
297cb93a386Sopenharmony_ci}
298cb93a386Sopenharmony_ci
299cb93a386Sopenharmony_ci- (BOOL)isOpaque {
300cb93a386Sopenharmony_ci    return YES;
301cb93a386Sopenharmony_ci}
302cb93a386Sopenharmony_ci
303cb93a386Sopenharmony_ci- (BOOL)canBecomeKeyView {
304cb93a386Sopenharmony_ci    return YES;
305cb93a386Sopenharmony_ci}
306cb93a386Sopenharmony_ci
307cb93a386Sopenharmony_ci- (BOOL)acceptsFirstResponder {
308cb93a386Sopenharmony_ci    return YES;
309cb93a386Sopenharmony_ci}
310cb93a386Sopenharmony_ci
311cb93a386Sopenharmony_ci- (void)updateTrackingAreas {
312cb93a386Sopenharmony_ci    if (fTrackingArea != nil) {
313cb93a386Sopenharmony_ci        [self removeTrackingArea:fTrackingArea];
314cb93a386Sopenharmony_ci        [fTrackingArea release];
315cb93a386Sopenharmony_ci    }
316cb93a386Sopenharmony_ci
317cb93a386Sopenharmony_ci    const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited |
318cb93a386Sopenharmony_ci                                          NSTrackingActiveInKeyWindow |
319cb93a386Sopenharmony_ci                                          NSTrackingEnabledDuringMouseDrag |
320cb93a386Sopenharmony_ci                                          NSTrackingCursorUpdate |
321cb93a386Sopenharmony_ci                                          NSTrackingInVisibleRect |
322cb93a386Sopenharmony_ci                                          NSTrackingAssumeInside;
323cb93a386Sopenharmony_ci
324cb93a386Sopenharmony_ci    fTrackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
325cb93a386Sopenharmony_ci                                                 options:options
326cb93a386Sopenharmony_ci                                                   owner:self
327cb93a386Sopenharmony_ci                                                userInfo:nil];
328cb93a386Sopenharmony_ci
329cb93a386Sopenharmony_ci    [self addTrackingArea:fTrackingArea];
330cb93a386Sopenharmony_ci    [super updateTrackingAreas];
331cb93a386Sopenharmony_ci}
332cb93a386Sopenharmony_ci
333cb93a386Sopenharmony_ci- (skui::ModifierKey) updateModifierKeys:(NSEvent*) event {
334cb93a386Sopenharmony_ci    using sknonstd::Any;
335cb93a386Sopenharmony_ci
336cb93a386Sopenharmony_ci    skui::ModifierKey modifiers = get_modifiers(event);
337cb93a386Sopenharmony_ci    skui::ModifierKey changed = modifiers ^ fLastModifiers;
338cb93a386Sopenharmony_ci    fLastModifiers = modifiers;
339cb93a386Sopenharmony_ci
340cb93a386Sopenharmony_ci    struct ModMap {
341cb93a386Sopenharmony_ci        skui::ModifierKey modifier;
342cb93a386Sopenharmony_ci        skui::Key         key;
343cb93a386Sopenharmony_ci    };
344cb93a386Sopenharmony_ci
345cb93a386Sopenharmony_ci    // Map each modifier bit to the equivalent skui Key and send key-up/down events.
346cb93a386Sopenharmony_ci    for (const ModMap& cur : {ModMap{skui::ModifierKey::kCommand, skui::Key::kSuper},
347cb93a386Sopenharmony_ci                              ModMap{skui::ModifierKey::kShift,   skui::Key::kShift},
348cb93a386Sopenharmony_ci                              ModMap{skui::ModifierKey::kControl, skui::Key::kCtrl},
349cb93a386Sopenharmony_ci                              ModMap{skui::ModifierKey::kOption,  skui::Key::kOption}}) {
350cb93a386Sopenharmony_ci        if (Any(changed & cur.modifier)) {
351cb93a386Sopenharmony_ci            const skui::InputState state = Any(modifiers & cur.modifier) ? skui::InputState::kDown
352cb93a386Sopenharmony_ci                                                                         : skui::InputState::kUp;
353cb93a386Sopenharmony_ci            (void) fWindow->onKey(cur.key, state, modifiers);
354cb93a386Sopenharmony_ci        }
355cb93a386Sopenharmony_ci    }
356cb93a386Sopenharmony_ci
357cb93a386Sopenharmony_ci    return modifiers;
358cb93a386Sopenharmony_ci}
359cb93a386Sopenharmony_ci
360cb93a386Sopenharmony_ci- (BOOL)performKeyEquivalent:(NSEvent *)event {
361cb93a386Sopenharmony_ci    [self updateModifierKeys:event];
362cb93a386Sopenharmony_ci
363cb93a386Sopenharmony_ci    // By default, unhandled key equivalents send -keyDown events; unfortunately, they do not send
364cb93a386Sopenharmony_ci    // a matching -keyUp. In other words, we can claim that we didn't handle the event and OS X will
365cb93a386Sopenharmony_ci    // turn this event into a -keyDown automatically, but we need to synthesize a matching -keyUp on
366cb93a386Sopenharmony_ci    // a later frame. Since we only read the modifiers and key code from the event, we can reuse
367cb93a386Sopenharmony_ci    // this "key-equivalent" event as a "key up".
368cb93a386Sopenharmony_ci    [self performSelector:@selector(keyUp:) withObject:event afterDelay:0.1];
369cb93a386Sopenharmony_ci    return NO;
370cb93a386Sopenharmony_ci}
371cb93a386Sopenharmony_ci
372cb93a386Sopenharmony_ci- (void)keyDown:(NSEvent *)event {
373cb93a386Sopenharmony_ci    skui::ModifierKey modifiers = [self updateModifierKeys:event];
374cb93a386Sopenharmony_ci
375cb93a386Sopenharmony_ci    skui::Key key = get_key([event keyCode]);
376cb93a386Sopenharmony_ci    if (key != skui::Key::kNONE) {
377cb93a386Sopenharmony_ci        if (!fWindow->onKey(key, skui::InputState::kDown, modifiers)) {
378cb93a386Sopenharmony_ci            if (skui::Key::kEscape == key) {
379cb93a386Sopenharmony_ci                [NSApp terminate:fWindow->window()];
380cb93a386Sopenharmony_ci            }
381cb93a386Sopenharmony_ci        }
382cb93a386Sopenharmony_ci    }
383cb93a386Sopenharmony_ci
384cb93a386Sopenharmony_ci    NSString* characters = [event charactersIgnoringModifiers];
385cb93a386Sopenharmony_ci    NSUInteger len = [characters length];
386cb93a386Sopenharmony_ci    if (len > 0) {
387cb93a386Sopenharmony_ci        unichar* charBuffer = new unichar[len+1];
388cb93a386Sopenharmony_ci        [characters getCharacters:charBuffer range:NSMakeRange(0, len)];
389cb93a386Sopenharmony_ci        for (NSUInteger i = 0; i < len; ++i) {
390cb93a386Sopenharmony_ci            (void) fWindow->onChar((SkUnichar) charBuffer[i], modifiers);
391cb93a386Sopenharmony_ci        }
392cb93a386Sopenharmony_ci        delete [] charBuffer;
393cb93a386Sopenharmony_ci    }
394cb93a386Sopenharmony_ci}
395cb93a386Sopenharmony_ci
396cb93a386Sopenharmony_ci- (void)keyUp:(NSEvent *)event {
397cb93a386Sopenharmony_ci    skui::ModifierKey modifiers = [self updateModifierKeys:event];
398cb93a386Sopenharmony_ci
399cb93a386Sopenharmony_ci    skui::Key key = get_key([event keyCode]);
400cb93a386Sopenharmony_ci    if (key != skui::Key::kNONE) {
401cb93a386Sopenharmony_ci        (void) fWindow->onKey(key, skui::InputState::kUp, modifiers);
402cb93a386Sopenharmony_ci    }
403cb93a386Sopenharmony_ci}
404cb93a386Sopenharmony_ci
405cb93a386Sopenharmony_ci-(void)flagsChanged:(NSEvent *)event {
406cb93a386Sopenharmony_ci    [self updateModifierKeys:event];
407cb93a386Sopenharmony_ci}
408cb93a386Sopenharmony_ci
409cb93a386Sopenharmony_ci- (void)mouseDown:(NSEvent *)event {
410cb93a386Sopenharmony_ci    NSView* view = fWindow->window().contentView;
411cb93a386Sopenharmony_ci    CGFloat backingScaleFactor = sk_app::GetBackingScaleFactor(view);
412cb93a386Sopenharmony_ci
413cb93a386Sopenharmony_ci    skui::ModifierKey modifiers = [self updateModifierKeys:event];
414cb93a386Sopenharmony_ci
415cb93a386Sopenharmony_ci    const NSPoint pos = [event locationInWindow];
416cb93a386Sopenharmony_ci    const NSRect rect = [view frame];
417cb93a386Sopenharmony_ci    fWindow->onMouse(pos.x * backingScaleFactor, (rect.size.height - pos.y) * backingScaleFactor,
418cb93a386Sopenharmony_ci                     skui::InputState::kDown, modifiers);
419cb93a386Sopenharmony_ci}
420cb93a386Sopenharmony_ci
421cb93a386Sopenharmony_ci- (void)mouseUp:(NSEvent *)event {
422cb93a386Sopenharmony_ci    NSView* view = fWindow->window().contentView;
423cb93a386Sopenharmony_ci    CGFloat backingScaleFactor = sk_app::GetBackingScaleFactor(view);
424cb93a386Sopenharmony_ci
425cb93a386Sopenharmony_ci    skui::ModifierKey modifiers = [self updateModifierKeys:event];
426cb93a386Sopenharmony_ci
427cb93a386Sopenharmony_ci    const NSPoint pos = [event locationInWindow];
428cb93a386Sopenharmony_ci    const NSRect rect = [view frame];
429cb93a386Sopenharmony_ci    fWindow->onMouse(pos.x * backingScaleFactor, (rect.size.height - pos.y) * backingScaleFactor,
430cb93a386Sopenharmony_ci                     skui::InputState::kUp, modifiers);
431cb93a386Sopenharmony_ci}
432cb93a386Sopenharmony_ci
433cb93a386Sopenharmony_ci- (void)mouseDragged:(NSEvent *)event {
434cb93a386Sopenharmony_ci    [self updateModifierKeys:event];
435cb93a386Sopenharmony_ci    [self mouseMoved:event];
436cb93a386Sopenharmony_ci}
437cb93a386Sopenharmony_ci
438cb93a386Sopenharmony_ci- (void)mouseMoved:(NSEvent *)event {
439cb93a386Sopenharmony_ci    NSView* view = fWindow->window().contentView;
440cb93a386Sopenharmony_ci    CGFloat backingScaleFactor = sk_app::GetBackingScaleFactor(view);
441cb93a386Sopenharmony_ci
442cb93a386Sopenharmony_ci    skui::ModifierKey modifiers = [self updateModifierKeys:event];
443cb93a386Sopenharmony_ci
444cb93a386Sopenharmony_ci    const NSPoint pos = [event locationInWindow];
445cb93a386Sopenharmony_ci    const NSRect rect = [view frame];
446cb93a386Sopenharmony_ci    fWindow->onMouse(pos.x * backingScaleFactor, (rect.size.height - pos.y) * backingScaleFactor,
447cb93a386Sopenharmony_ci                     skui::InputState::kMove, modifiers);
448cb93a386Sopenharmony_ci}
449cb93a386Sopenharmony_ci
450cb93a386Sopenharmony_ci- (void)scrollWheel:(NSEvent *)event {
451cb93a386Sopenharmony_ci    skui::ModifierKey modifiers = [self updateModifierKeys:event];
452cb93a386Sopenharmony_ci
453cb93a386Sopenharmony_ci    // TODO: support hasPreciseScrollingDeltas?
454cb93a386Sopenharmony_ci    fWindow->onMouseWheel([event scrollingDeltaY], modifiers);
455cb93a386Sopenharmony_ci}
456cb93a386Sopenharmony_ci
457cb93a386Sopenharmony_ci- (void)drawRect:(NSRect)rect {
458cb93a386Sopenharmony_ci    fWindow->onPaint();
459cb93a386Sopenharmony_ci}
460cb93a386Sopenharmony_ci
461cb93a386Sopenharmony_ci@end
462