Flutter Engine
The Flutter Engine
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:
128 break;
129#endif
130#ifdef SK_DAWN
131#if defined(SK_GRAPHITE)
132 case kGraphiteDawn_BackendType:
134 break;
135#endif
136#endif
137#ifdef SK_VULKAN
138 case kVulkan_BackendType:
139 fWindowContext = MakeVulkanForMac(info, fRequestedDisplayParams);
140 break;
141#if defined(SK_GRAPHITE)
142 case kGraphiteVulkan_BackendType:
143 fWindowContext = nullptr;
144 return false;
145#endif
146#endif
147#ifdef SK_METAL
148 case kMetal_BackendType:
150 break;
151#if defined(SK_GRAPHITE)
152 case kGraphiteMetal_BackendType:
154 break;
155#endif
156#endif
157#ifdef SK_GL
158 case kNativeGL_BackendType:
160 break;
163 break;
164#endif
165 default:
166 SkASSERT_RELEASE(false);
167 }
168 this->onBackendCreated();
169
170 return SkToBool(fWindowContext);
171}
172
174 return skwindow::GetBackingScaleFactor(fWindow.contentView);
175}
176
178 gWindowMap.foreach([&](Window_mac* window) {
179 if (window->fIsContentInvalidated) {
180 window->onPaint();
181 }
182 });
183}
184
185} // namespace sk_app
186
187///////////////////////////////////////////////////////////////////////////////
188
189@implementation WindowDelegate {
190 sk_app::Window_mac* fWindow;
191}
192
193- (WindowDelegate*)initWithWindow:(sk_app::Window_mac *)initWindow {
194 fWindow = initWindow;
195
196 return self;
197}
198
199- (void)windowDidResize:(NSNotification *)notification {
200 NSView* view = fWindow->window().contentView;
202 fWindow->onResize(view.bounds.size.width * scale, view.bounds.size.height * scale);
203 fWindow->inval();
204}
205
206- (BOOL)windowShouldClose:(NSWindow*)sender {
207 fWindow->closeWindow();
208
209 return FALSE;
210}
211
212@end
213
214///////////////////////////////////////////////////////////////////////////////
215
216static skui::Key get_key(unsigned short vk) {
217 // This will work with an ANSI QWERTY keyboard.
218 // Something more robust would be needed to support alternate keyboards.
219 static const struct {
220 unsigned short fVK;
221 skui::Key fKey;
222 } gPair[] = {
223 { kVK_Delete, skui::Key::kBack },
224 { kVK_Return, skui::Key::kOK },
225 { kVK_UpArrow, skui::Key::kUp },
226 { kVK_DownArrow, skui::Key::kDown },
227 { kVK_LeftArrow, skui::Key::kLeft },
228 { kVK_RightArrow, skui::Key::kRight },
229 { kVK_Tab, skui::Key::kTab },
230 { kVK_PageUp, skui::Key::kPageUp },
231 { kVK_PageDown, skui::Key::kPageDown },
232 { kVK_Home, skui::Key::kHome },
233 { kVK_End, skui::Key::kEnd },
234 { kVK_ForwardDelete, skui::Key::kDelete },
235 { kVK_Escape, skui::Key::kEscape },
236 { kVK_Shift, skui::Key::kShift },
237 { kVK_RightShift, skui::Key::kShift },
238 { kVK_Control, skui::Key::kCtrl },
239 { kVK_RightControl, skui::Key::kCtrl },
240 { kVK_Option, skui::Key::kOption },
241 { kVK_RightOption, skui::Key::kOption },
242 { kVK_Command, skui::Key::kSuper },
243 { kVK_RightCommand, skui::Key::kSuper },
244 { kVK_ANSI_A, skui::Key::kA },
245 { kVK_ANSI_C, skui::Key::kC },
246 { kVK_ANSI_V, skui::Key::kV },
247 { kVK_ANSI_X, skui::Key::kX },
248 { kVK_ANSI_Y, skui::Key::kY },
249 { kVK_ANSI_Z, skui::Key::kZ },
250 };
251
252 for (size_t i = 0; i < std::size(gPair); i++) {
253 if (gPair[i].fVK == vk) {
254 return gPair[i].fKey;
255 }
256 }
257
258 return skui::Key::kNONE;
259}
260
261static skui::ModifierKey get_modifiers(const NSEvent* event) {
262 NSUInteger modifierFlags = [event modifierFlags];
264
265 if (modifierFlags & NSEventModifierFlagCommand) {
266 modifiers |= skui::ModifierKey::kCommand;
267 }
268 if (modifierFlags & NSEventModifierFlagShift) {
269 modifiers |= skui::ModifierKey::kShift;
270 }
271 if (modifierFlags & NSEventModifierFlagControl) {
272 modifiers |= skui::ModifierKey::kControl;
273 }
274 if (modifierFlags & NSEventModifierFlagOption) {
275 modifiers |= skui::ModifierKey::kOption;
276 }
277
278 if ((NSKeyDown == [event type] || NSKeyUp == [event type]) && ![event isARepeat]) {
280 }
281
282 return modifiers;
283}
284
285@implementation MainView {
286 sk_app::Window_mac* fWindow;
287 // A TrackingArea prevents us from capturing events outside the view
288 NSTrackingArea* fTrackingArea;
289 // We keep track of the state of the modifier keys on each event in order to synthesize
290 // key-up/down events for each modifier.
292}
293
294- (MainView*)initWithWindow:(sk_app::Window_mac *)initWindow {
295 self = [super init];
296
297 fWindow = initWindow;
298 fTrackingArea = nil;
299
300 [self updateTrackingAreas];
301
302 return self;
303}
304
305- (void)dealloc
306{
307 [fTrackingArea release];
308 [super dealloc];
309}
310
311- (BOOL)isOpaque {
312 return YES;
313}
314
315- (BOOL)canBecomeKeyView {
316 return YES;
317}
318
319- (BOOL)acceptsFirstResponder {
320 return YES;
321}
322
323- (void)updateTrackingAreas {
324 if (fTrackingArea != nil) {
325 [self removeTrackingArea:fTrackingArea];
326 [fTrackingArea release];
327 }
328
329 const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited |
330 NSTrackingActiveInKeyWindow |
331 NSTrackingEnabledDuringMouseDrag |
332 NSTrackingCursorUpdate |
333 NSTrackingInVisibleRect |
334 NSTrackingAssumeInside;
335
336 fTrackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
337 options:options
338 owner:self
339 userInfo:nil];
340
341 [self addTrackingArea:fTrackingArea];
342 [super updateTrackingAreas];
343}
344
345- (skui::ModifierKey) updateModifierKeys:(NSEvent*) event {
346 using sknonstd::Any;
347
349 skui::ModifierKey changed = modifiers ^ fLastModifiers;
350 fLastModifiers = modifiers;
351
352 struct ModMap {
353 skui::ModifierKey modifier;
355 };
356
357 // Map each modifier bit to the equivalent skui Key and send key-up/down events.
358 for (const ModMap& cur : {ModMap{skui::ModifierKey::kCommand, skui::Key::kSuper},
362 if (Any(changed & cur.modifier)) {
363 const skui::InputState state = Any(modifiers & cur.modifier) ? skui::InputState::kDown
365 (void) fWindow->onKey(cur.key, state, modifiers);
366 }
367 }
368
369 return modifiers;
370}
371
372- (BOOL)performKeyEquivalent:(NSEvent *)event {
373 [self updateModifierKeys:event];
374
375 // By default, unhandled key equivalents send -keyDown events; unfortunately, they do not send
376 // a matching -keyUp. In other words, we can claim that we didn't handle the event and OS X will
377 // turn this event into a -keyDown automatically, but we need to synthesize a matching -keyUp on
378 // a later frame. Since we only read the modifiers and key code from the event, we can reuse
379 // this "key-equivalent" event as a "key up".
380 [self performSelector:@selector(keyUp:) withObject:event afterDelay:0.1];
381 return NO;
382}
383
384- (void)keyDown:(NSEvent *)event {
385 skui::ModifierKey modifiers = [self updateModifierKeys:event];
386
387 skui::Key key = get_key([event keyCode]);
388 if (key != skui::Key::kNONE) {
389 if (!fWindow->onKey(key, skui::InputState::kDown, modifiers)) {
390 if (skui::Key::kEscape == key) {
391 [NSApp terminate:fWindow->window()];
392 }
393 }
394 }
395
396 NSString* characters = [event charactersIgnoringModifiers];
397 NSUInteger len = [characters length];
398 if (len > 0) {
399 unichar* charBuffer = new unichar[len+1];
400 [characters getCharacters:charBuffer range:NSMakeRange(0, len)];
401 for (NSUInteger i = 0; i < len; ++i) {
402 (void) fWindow->onChar((SkUnichar) charBuffer[i], modifiers);
403 }
404 delete [] charBuffer;
405 }
406}
407
408- (void)keyUp:(NSEvent *)event {
409 skui::ModifierKey modifiers = [self updateModifierKeys:event];
410
411 skui::Key key = get_key([event keyCode]);
412 if (key != skui::Key::kNONE) {
413 (void) fWindow->onKey(key, skui::InputState::kUp, modifiers);
414 }
415}
416
417-(void)flagsChanged:(NSEvent *)event {
418 [self updateModifierKeys:event];
419}
420
421- (void)mouseDown:(NSEvent *)event {
422 NSView* view = fWindow->window().contentView;
423 CGFloat backingScaleFactor = skwindow::GetBackingScaleFactor(view);
424
425 skui::ModifierKey modifiers = [self updateModifierKeys:event];
426
427 const NSPoint pos = [event locationInWindow];
428 const NSRect rect = [view frame];
429 fWindow->onMouse(pos.x * backingScaleFactor, (rect.size.height - pos.y) * backingScaleFactor,
430 skui::InputState::kDown, modifiers);
431}
432
433- (void)mouseUp:(NSEvent *)event {
434 NSView* view = fWindow->window().contentView;
435 CGFloat backingScaleFactor = skwindow::GetBackingScaleFactor(view);
436
437 skui::ModifierKey modifiers = [self updateModifierKeys:event];
438
439 const NSPoint pos = [event locationInWindow];
440 const NSRect rect = [view frame];
441 fWindow->onMouse(pos.x * backingScaleFactor, (rect.size.height - pos.y) * backingScaleFactor,
442 skui::InputState::kUp, modifiers);
443}
444
445- (void)mouseDragged:(NSEvent *)event {
446 [self updateModifierKeys:event];
447 [self mouseMoved:event];
448}
449
450- (void)mouseMoved:(NSEvent *)event {
451 NSView* view = fWindow->window().contentView;
452 CGFloat backingScaleFactor = skwindow::GetBackingScaleFactor(view);
453
454 skui::ModifierKey modifiers = [self updateModifierKeys:event];
455
456 const NSPoint pos = [event locationInWindow];
457 const NSRect rect = [view frame];
458 fWindow->onMouse(pos.x * backingScaleFactor, (rect.size.height - pos.y) * backingScaleFactor,
459 skui::InputState::kMove, modifiers);
460}
461
462- (void)scrollWheel:(NSEvent *)event {
463 NSView* view = fWindow->window().contentView;
464 CGFloat backingScaleFactor = skwindow::GetBackingScaleFactor(view);
465
466 skui::ModifierKey modifiers = [self updateModifierKeys:event];
467
468 // TODO: support hasPreciseScrollingDeltas?
469 const NSPoint pos = [event locationInWindow];
470 const NSRect rect = [view frame];
471 fWindow->onMouseWheel([event scrollingDeltaY],
472 pos.x * backingScaleFactor,
473 (rect.size.height - pos.y) * backingScaleFactor,
474 modifiers);
475}
476
477- (void)drawRect:(NSRect)rect {
478 fWindow->onPaint();
479}
480
481@end
const char * options
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
int count
Definition: FontMgrTest.cpp:50
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)
Definition: Window_mac.mm:261
static skui::Key get_key(unsigned short vk)
Definition: Window_mac.mm:216
NSTrackingArea * fTrackingArea
Definition: Window_mac.mm:288
skui::ModifierKey fLastModifiers
Definition: Window_mac.mm:291
GLenum type
void setTitle(const char *) override
Definition: Window_mac.mm:106
float scaleFactor() const override
Definition: Window_mac.mm:173
NSWindow * window()
Definition: Window_mac.h:48
void show() override
Definition: Window_mac.mm:112
static void PaintWindows()
Definition: Window_mac.mm:177
bool attach(BackendType) override
Definition: Window_mac.mm:119
static Window * CreateNativeWindow(void *platformData)
@ kRaster_BackendType
Definition: Window.h:90
DisplayParams fRequestedDisplayParams
Definition: Window.h:176
std::unique_ptr< skwindow::WindowContext > fWindowContext
Definition: Window.h:179
void onBackendCreated()
Definition: Window.cpp:44
GLFWwindow * window
Definition: main.cc:45
AtkStateType state
FlKeyEvent * event
Definition: dart.idl:411
return FALSE
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
vk
Definition: malisc.py:42
std::enable_if_t< sknonstd::is_bitmask_enum< E >::value, bool > constexpr Any(E e)
Definition: SkBitmaskEnum.h:16
InputState
Definition: InputState.h:6
ModifierKey
Definition: ModifierKey.h:9
Key
Definition: Key.h:6
@ kBack
(CLR)
@ kHome
the home key - added to match android
@ kEnd
the red key
@ kOK
the center key
std::unique_ptr< WindowContext > MakeGraphiteDawnMetalForMac(const MacWindowInfo &info, const DisplayParams &params)
std::unique_ptr< WindowContext > MakeGLForMac(const MacWindowInfo &info, const DisplayParams &params)
std::unique_ptr< WindowContext > MakeRasterForMac(const MacWindowInfo &info, const DisplayParams &params)
std::unique_ptr< WindowContext > MakeMetalForMac(const MacWindowInfo &info, const DisplayParams &params)
std::unique_ptr< WindowContext > MakeANGLEForMac(const MacWindowInfo &info, const DisplayParams &params)
static CGFloat GetBackingScaleFactor(NSView *view)
std::unique_ptr< WindowContext > MakeGraphiteMetalForMac(const MacWindowInfo &info, const DisplayParams &params)
const Scalar scale
constexpr float y() const
Definition: SkPoint_impl.h:187
constexpr float x() const
Definition: SkPoint_impl.h:181
int BOOL
Definition: windows_types.h:37