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