Fix IME Window is not shown issue.
[platform/core/uifw/dali-adaptor.git] / dali / internal / window-system / macos / window-base-mac.mm
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include "dali/public-api/adaptor-framework/window.h"
19 #include "dali/public-api/events/wheel-event.h"
20 #include <Carbon/Carbon.h>
21 #import <Cocoa/Cocoa.h>
22
23 // CLASS HEADER
24 #include <dali/internal/window-system/macos/window-base-mac.h>
25
26 // EXTERNAL_HEADERS
27 #include <dali/public-api/object/any.h>
28 #include <dali/integration-api/debug.h>
29
30 // INTERNAL HEADERS
31 #include <dali/internal/window-system/common/window-impl.h>
32 #include <dali/internal/window-system/common/window-render-surface.h>
33 #include <dali/internal/window-system/common/window-system.h>
34
35 #include <cmath>
36
37 using Dali::Internal::Adaptor::WindowBaseCocoa;
38
39 // Angle is default selecting CGL as its backend and because
40 // of that we are using NSOpenGLView. Ideally we should use
41 // Metal as the backend. When this happends, we must change
42 // the parent class to MTKView.
43 @interface CocoaView : NSOpenGLView
44 - (CocoaView *) initWithFrame:(NSRect) rect withImpl:(WindowBaseCocoa::Impl *) impl;
45 - (BOOL) isFlipped;
46 - (BOOL) wantsUpdateLayer;
47 - (BOOL) acceptsFirstResponder;
48 - (void) mouseDown:(NSEvent *) event;
49 - (void) mouseUp:(NSEvent *) event;
50 - (void) mouseDragged:(NSEvent *) event;
51 - (void) keyDown:(NSEvent *) event;
52 - (void) keyUp:(NSEvent *) event;
53 - (void) drawRect:(NSRect) dirtyRect;
54 - (void) prepareOpenGL;
55 @end
56
57 @interface WindowDelegate : NSObject <NSWindowDelegate>
58 - (WindowDelegate *) init:(WindowBaseCocoa::Impl *) impl;
59 - (void) windowDidBecomeKey:(NSNotification *) notification;
60 - (void) windowDidResignKey:(NSNotification *) notification;
61 - (void) windowWillClose:(NSNotification *) notification;
62 @end
63
64 namespace Dali::Internal::Adaptor
65 {
66
67 namespace
68 {
69
70 #if defined(DEBUG_ENABLED)
71 Debug::Filter* gWindowBaseLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_WINDOW_BASE" );
72 #endif
73
74 // Converts a y coordinate from top to bottom coordinate
75 CGFloat BottomYCoordinate(CGFloat topYCoordinate, CGFloat windowHeight) noexcept
76 {
77   const auto screen = [NSScreen.mainScreen frame];
78   return screen.size.height - windowHeight - topYCoordinate;
79 }
80
81 NSRect PositionSizeToRect(const PositionSize &positionSize, bool flipped = false) noexcept
82 {
83   // positionSize assumes top-left coordinate system
84   // Cocoa assumes bottom-left coordinate system
85   // If NSView isFlipped method returns YES, then it uses top-left coordinate system
86   const auto windowHeight = static_cast<CGFloat>(positionSize.height);
87   const auto yGiven = static_cast<CGFloat>(positionSize.y);
88
89   CGFloat yWindow;
90   if (flipped)
91   {
92     yWindow = yGiven;
93   }
94   else
95   {
96     yWindow = BottomYCoordinate(yGiven, windowHeight);
97   }
98
99   return
100   {
101     .origin =
102     {
103       .x = static_cast<CGFloat>(positionSize.x),
104       .y = yWindow
105     },
106     .size =
107     {
108       .width = static_cast<CGFloat>(positionSize.width),
109       .height = windowHeight,
110     },
111   };
112 }
113
114 } // unnamed namespace
115
116 struct WindowBaseCocoa::Impl final
117 {
118   NSWindow *mWindow;
119   NSWindowController *mWinController;
120   WindowBaseCocoa *mThis;
121
122   Impl(const Impl &rhs) = delete;
123   Impl &operator<(const Impl &rhs) = delete;
124   Impl(const Impl &&rhs) = delete;
125   Impl &operator<(const Impl &&rhs) = delete;
126
127   Impl(
128     WindowBaseCocoa *pThis,
129     PositionSize positionSize,
130     Any surface,
131     bool isTransparent
132   );
133
134   ~Impl();
135
136   void OnFocus(bool focus)
137   {
138     mThis->mFocusChangedSignal.Emit(focus);
139   }
140
141   // Handle mouse events
142   void OnMouse(NSEvent *event, PointState::Type state);
143   void OnMouseWheel(NSEvent *event);
144   void OnKey(NSEvent *event, Integration::KeyEvent::State keyState);
145   void OnWindowDamaged(const NSRect &rect);
146
147   void OnRedraw(void)
148   {
149     mThis->mWindowRedrawRequestSignal.Emit();
150   }
151
152 private:
153   uint32_t GetKeyModifiers(NSEvent *event) const noexcept;
154   std::string GetKeyName(NSEvent *event) const;
155 };
156
157 WindowBaseCocoa::Impl::Impl(
158   WindowBaseCocoa *pThis,
159   PositionSize positionSize,
160   Any surface,
161   bool isTransparent
162 ) : mThis(pThis)
163 {
164   constexpr NSUInteger style =
165     NSWindowStyleMaskTitled
166     | NSWindowStyleMaskClosable
167     | NSWindowStyleMaskMiniaturizable
168     | NSWindowStyleMaskResizable;
169
170   mWindow = [[NSWindow alloc] initWithContentRect:PositionSizeToRect(positionSize)
171                                         styleMask:style
172                                           backing:NSBackingStoreBuffered
173                                             defer:NO];
174
175   mWindow.alphaValue = static_cast<CGFloat>(!isTransparent);
176   mWinController = [[NSWindowController alloc] initWithWindow:mWindow];
177
178   mWindow.delegate = [[WindowDelegate alloc] init:this];
179
180   NSView *view = [[CocoaView alloc] initWithFrame:PositionSizeToRect(positionSize, true)
181                                          withImpl:this];
182   NSPoint origin{0, 0};
183   [view setFrameOrigin:origin];
184
185   mWindow.contentView = view;
186
187   [mWindow makeKeyAndOrderFront:nil];
188 }
189
190 WindowBaseCocoa::Impl::~Impl()
191 {
192   [mWinController close];
193   [NSApp stop:nil];
194 }
195
196 void WindowBaseCocoa::Impl::OnMouse(NSEvent *event, PointState::Type state)
197 {
198   Integration::Point point;
199   point.SetDeviceId(event.deviceID);
200   point.SetState(state);
201   auto p = [event locationInWindow];
202   auto [x, y] = [mWindow.contentView convertPoint:p fromView:nil];
203   point.SetScreenPosition(Vector2(x, y));
204   point.SetRadius(std::sqrt(x*x + y*y));
205   point.SetPressure(event.pressure);
206
207   if (x == 0.0)
208   {
209     point.SetAngle(Degree(0.0));
210   }
211   else
212   {
213     point.SetAngle(Radian(std::atan(y/x)));
214   }
215
216   DALI_LOG_INFO(
217     gWindowBaseLogFilter,
218     Debug::Verbose,
219     "WindowBaseCocoa::Impl::OnMouse(%.1f, %.1f)\n",
220     x,
221     y
222   );
223
224   // timestamp is given in seconds, the signal expects it in milliseconds
225   mThis->mTouchEventSignal.Emit(point, event.timestamp * 1000);
226 }
227
228 void WindowBaseCocoa::Impl::OnMouseWheel(NSEvent *event)
229 {
230   auto p = [event locationInWindow];
231   auto [x, y] = [mWindow.contentView convertPoint:p fromView:nil];
232
233   const auto modifiers = GetKeyModifiers(event);
234   const Vector2 vec(x, y);
235   const auto timestamp = event.timestamp * 1000;
236
237   if (event.scrollingDeltaY)
238   {
239     Integration::WheelEvent wheelEvent(
240       Integration::WheelEvent::MOUSE_WHEEL,
241       0,
242       modifiers,
243       vec,
244       event.scrollingDeltaY < 0 ? -1 : 1,
245       timestamp
246     );
247
248     mThis->mWheelEventSignal.Emit(wheelEvent);
249   }
250
251   if (event.scrollingDeltaX)
252   {
253     Integration::WheelEvent wheelEvent(
254       Integration::WheelEvent::MOUSE_WHEEL,
255       0,
256       modifiers,
257       vec,
258       event.scrollingDeltaX < 0 ? -1 : 1,
259       timestamp
260     );
261
262     mThis->mWheelEventSignal.Emit(wheelEvent);
263   }
264 }
265
266 void WindowBaseCocoa::Impl::OnKey(NSEvent *event, Integration::KeyEvent::State keyState)
267 {
268   const std::string empty;
269
270   Integration::KeyEvent keyEvent(
271     GetKeyName(event),
272     empty,
273     [event.characters UTF8String],
274     event.keyCode,
275     GetKeyModifiers(event),
276     event.timestamp * 1000,
277     keyState,
278     empty,
279     empty,
280     Device::Class::NONE,
281     Device::Subclass::NONE
282   );
283
284   DALI_LOG_INFO(
285     gWindowBaseLogFilter,
286     Debug::Verbose,
287     "WindowBaseCocoa::Impl::OnKey(%s)\n",
288     [event.characters UTF8String]
289   );
290
291   mThis->mKeyEventSignal.Emit(keyEvent);
292 }
293
294 void WindowBaseCocoa::Impl::OnWindowDamaged(const NSRect &rect)
295 {
296   const DamageArea area(
297     rect.origin.x,
298     rect.origin.y,
299     rect.size.width,
300     rect.size.height
301   );
302
303   mThis->mWindowDamagedSignal.Emit(area);
304 }
305
306 uint32_t WindowBaseCocoa::Impl::GetKeyModifiers(NSEvent *event) const noexcept
307 {
308   uint32_t modifiers = 0;
309
310   if (event.modifierFlags & NSEventModifierFlagShift)
311   {
312     modifiers |= 1;
313   }
314
315   if (event.modifierFlags & NSEventModifierFlagControl)
316   {
317     modifiers |= 2;
318   }
319
320   if (event.modifierFlags & NSEventModifierFlagCommand)
321   {
322     modifiers |= 4;
323   }
324
325   return modifiers;
326 }
327
328 std::string WindowBaseCocoa::Impl::GetKeyName(NSEvent *event) const
329 {
330   switch (event.keyCode)
331   {
332     case kVK_Control:     return "Control";
333     case kVK_Shift:       return "Shift";
334     case kVK_Delete:      return "Backspace";
335     case kVK_Command:     return "Command";
336     case kVK_Tab:         return "Tab";
337     case kVK_Return:      return "Return";
338     case kVK_Escape:      return "Escape";
339     case kVK_Space:       return "Space";
340     case kVK_LeftArrow:   return "Left";
341     case kVK_UpArrow:     return "Up";
342     case kVK_RightArrow:  return "Right";
343     case kVK_DownArrow:   return "Down";
344     case kVK_ANSI_0:      return "0";
345     case kVK_ANSI_1:      return "1";
346     case kVK_ANSI_2:      return "2";
347     case kVK_ANSI_3:      return "3";
348     case kVK_ANSI_4:      return "4";
349     case kVK_ANSI_5:      return "5";
350     case kVK_ANSI_6:      return "6";
351     case kVK_ANSI_7:      return "7";
352     case kVK_ANSI_8:      return "8";
353     case kVK_ANSI_9:      return "9";
354     default:              return [event.characters UTF8String];
355   }
356
357   return "";
358 }
359
360 WindowBaseCocoa::WindowBaseCocoa(PositionSize positionSize, Any surface, bool isTransparent)
361   : mImpl(std::make_unique<Impl>(this, positionSize, surface, isTransparent))
362 {
363 }
364
365 WindowBaseCocoa::~WindowBaseCocoa()
366 {
367 }
368
369 Any WindowBaseCocoa::GetNativeWindow()
370 {
371   return mImpl->mWindow;
372 }
373
374 int WindowBaseCocoa::GetNativeWindowId()
375 {
376   return mImpl->mWindow.windowNumber;
377 }
378
379 std::string WindowBaseCocoa::GetNativeWindowResourceId()
380 {
381   return std::string();
382 }
383
384 EGLNativeWindowType WindowBaseCocoa::CreateEglWindow(int width, int height)
385 {
386   // XXX: this method is called from a secondary thread, but
387   // we can only resize the window from the main thread
388   //PositionSize size(0, 0, width, height);
389   //Resize(size);
390   return mImpl->mWindow.contentView.layer;
391 }
392
393 void WindowBaseCocoa::DestroyEglWindow()
394 {
395 }
396
397 void WindowBaseCocoa::SetEglWindowRotation( int angle )
398 {
399 }
400
401 void WindowBaseCocoa::SetEglWindowBufferTransform( int angle )
402 {
403 }
404
405 void WindowBaseCocoa::SetEglWindowTransform( int angle )
406 {
407 }
408
409 void WindowBaseCocoa::ResizeEglWindow( PositionSize positionSize )
410 {
411   dispatch_async(dispatch_get_main_queue(), ^{
412     Resize(positionSize);
413   });
414 }
415
416 bool WindowBaseCocoa::IsEglWindowRotationSupported()
417 {
418   return false;
419 }
420
421 void WindowBaseCocoa::Move( PositionSize positionSize )
422 {
423   const NSPoint p = {
424     .x = static_cast<CGFloat>(positionSize.x),
425     .y = static_cast<CGFloat>(positionSize.y),
426   };
427
428   [mImpl->mWindow setFrameTopLeftPoint:p];
429 }
430
431 void WindowBaseCocoa::Resize( PositionSize positionSize )
432 {
433   auto r = mImpl->mWindow.frame;
434   r.size.width = static_cast<CGFloat>(positionSize.width);
435   r.size.height = static_cast<CGFloat>(positionSize.height);
436   [mImpl->mWindow setFrame:r display:YES];
437
438   NSSize size =
439   {
440     .width = r.size.width,
441     .height = r.size.height,
442   };
443
444   [mImpl->mWindow.contentView setFrameSize:size];
445
446 }
447
448 void WindowBaseCocoa::MoveResize( PositionSize positionSize )
449 {
450   [mImpl->mWindow setFrame: PositionSizeToRect(positionSize) display:YES];
451
452   NSSize size =
453   {
454     .width = static_cast<CGFloat>(positionSize.width),
455     .height = static_cast<CGFloat>(positionSize.height),
456   };
457
458   [mImpl->mWindow.contentView setFrameSize:size];
459 }
460
461 void WindowBaseCocoa::SetLayout(unsigned int numCols, unsigned int numRows, unsigned int column, unsigned int row, unsigned int colSpan, unsigned int rowSpan)
462 {
463 }
464
465 void WindowBaseCocoa::SetClass( const std::string& name, const std::string& className )
466 {
467 }
468
469 void WindowBaseCocoa::Raise()
470 {
471   [mImpl->mWindow orderFront:nil];
472 }
473
474 void WindowBaseCocoa::Lower()
475 {
476   [mImpl->mWindow orderBack:nil];
477 }
478
479 void WindowBaseCocoa::Activate()
480 {
481   [mImpl->mWinController showWindow:nil];
482 }
483
484 void WindowBaseCocoa::Maximize(bool maximize)
485 {
486 }
487
488 bool WindowBaseCocoa::IsMaximized() const
489 {
490   return false;
491 }
492
493 void WindowBaseCocoa::SetMaximumSize(Dali::Window::WindowSize size)
494 {
495 }
496
497 void WindowBaseCocoa::Minimize(bool minimize)
498 {
499 }
500
501 bool WindowBaseCocoa::IsMinimized() const
502 {
503   return false;
504 }
505
506 void WindowBaseCocoa::SetMimimumSize(Dali::Window::WindowSize size)
507 {
508 }
509
510 void WindowBaseCocoa::SetAvailableAnlges( const std::vector< int >& angles )
511 {
512 }
513
514 void WindowBaseCocoa::SetPreferredAngle( int angle )
515 {
516 }
517
518 void WindowBaseCocoa::SetAcceptFocus( bool accept )
519 {
520 }
521
522 void WindowBaseCocoa::Show()
523 {
524   [mImpl->mWinController showWindow:nil];
525 }
526
527 void WindowBaseCocoa::Hide()
528 {
529   [mImpl->mWindow orderOut:nil];
530 }
531
532 unsigned int WindowBaseCocoa::GetSupportedAuxiliaryHintCount() const
533 {
534   return 0;
535 }
536
537 std::string WindowBaseCocoa::GetSupportedAuxiliaryHint( unsigned int index ) const
538 {
539   return std::string();
540 }
541
542 unsigned int WindowBaseCocoa::AddAuxiliaryHint( const std::string& hint, const std::string& value )
543 {
544   return 0;
545 }
546
547 bool WindowBaseCocoa::RemoveAuxiliaryHint( unsigned int id )
548 {
549   return false;
550 }
551
552 bool WindowBaseCocoa::SetAuxiliaryHintValue( unsigned int id, const std::string& value )
553 {
554   return false;
555 }
556
557 std::string WindowBaseCocoa::GetAuxiliaryHintValue( unsigned int id ) const
558 {
559   return std::string();
560 }
561
562 unsigned int WindowBaseCocoa::GetAuxiliaryHintId( const std::string& hint ) const
563 {
564   return 0;
565 }
566
567 void WindowBaseCocoa::SetInputRegion( const Rect< int >& inputRegion )
568 {
569 }
570
571 void WindowBaseCocoa::SetType( Dali::WindowType type )
572 {
573 }
574
575 Dali::WindowType WindowBaseCocoa::GetType() const
576 {
577   return Dali::WindowType::NORMAL;
578 }
579
580 WindowOperationResult WindowBaseCocoa::SetNotificationLevel( WindowNotificationLevel level )
581 {
582   return WindowOperationResult::NOT_SUPPORTED;
583 }
584
585 WindowNotificationLevel WindowBaseCocoa::GetNotificationLevel() const
586 {
587   return WindowNotificationLevel::NONE;
588 }
589
590 void WindowBaseCocoa::SetOpaqueState( bool opaque )
591 {
592 }
593
594 WindowOperationResult WindowBaseCocoa::SetScreenOffMode(WindowScreenOffMode screenOffMode)
595 {
596   return WindowOperationResult::NOT_SUPPORTED;
597 }
598
599 WindowScreenOffMode WindowBaseCocoa::GetScreenOffMode() const
600 {
601   return WindowScreenOffMode::TIMEOUT;
602 }
603
604 WindowOperationResult WindowBaseCocoa::SetBrightness( int brightness )
605 {
606   return WindowOperationResult::NOT_SUPPORTED;
607 }
608
609 int WindowBaseCocoa::GetBrightness() const
610 {
611   return 0;
612 }
613
614 bool WindowBaseCocoa::GrabKey( Dali::KEY key, KeyGrab::KeyGrabMode grabMode )
615 {
616   return false;
617 }
618
619 bool WindowBaseCocoa::UngrabKey( Dali::KEY key )
620 {
621   return false;
622 }
623
624 bool WindowBaseCocoa::GrabKeyList(
625   const Dali::Vector< Dali::KEY >& key,
626   const Dali::Vector< KeyGrab::KeyGrabMode >& grabMode,
627   Dali::Vector< bool >& result
628 )
629 {
630   return false;
631 }
632
633 bool WindowBaseCocoa::UngrabKeyList(
634   const Dali::Vector< Dali::KEY >& key,
635   Dali::Vector< bool >& result
636 )
637 {
638   return false;
639 }
640
641 void WindowBaseCocoa::GetDpi(
642   unsigned int& dpiHorizontal,
643   unsigned int& dpiVertical
644 )
645 {
646   auto *screen = [NSScreen mainScreen];
647   NSSize res = [screen.deviceDescription[NSDeviceResolution] sizeValue];
648   dpiHorizontal = res.width;
649   dpiVertical = res.height;
650 }
651
652 int WindowBaseCocoa::GetWindowRotationAngle() const
653 {
654   return 0;
655 }
656
657 int WindowBaseCocoa::GetScreenRotationAngle()
658 {
659   return 0;
660 }
661
662 void WindowBaseCocoa::SetWindowRotationAngle( int degree )
663 {
664 }
665
666 void WindowBaseCocoa::WindowRotationCompleted( int degree, int width, int height )
667 {
668 }
669
670 void WindowBaseCocoa::SetTransparency( bool transparent )
671 {
672   mImpl->mWindow.alphaValue = static_cast<CGFloat>(!transparent);
673 }
674
675 void WindowBaseCocoa::SetParent(WindowBase* parentWinBase, bool belowParent)
676 {
677   auto &parent = dynamic_cast<WindowBaseCocoa&>(*parentWinBase);
678   [mImpl->mWindow setParentWindow:parent.mImpl->mWindow];
679 }
680
681 int WindowBaseCocoa::CreateFrameRenderedSyncFence()
682 {
683   return -1;
684 }
685
686 int WindowBaseCocoa::CreateFramePresentedSyncFence()
687 {
688   return -1;
689 }
690
691 void WindowBaseCocoa::SetPositionSizeWithAngle(PositionSize positionSize, int angle)
692 {
693 }
694
695 void WindowBaseCocoa::InitializeIme()
696 {
697 }
698
699 void WindowBaseCocoa::ImeWindowReadyToRender()
700 {
701 }
702
703 void WindowBaseCocoa::RequestMoveToServer()
704 {
705 }
706
707 void WindowBaseCocoa::RequestResizeToServer(WindowResizeDirection direction)
708 {
709 }
710
711 void WindowBaseCocoa::EnableFloatingMode(bool enable)
712 {
713 }
714
715 bool WindowBaseCocoa::IsFloatingModeEnabled() const
716 {
717   return false;
718 }
719
720 void WindowBaseCocoa::IncludeInputRegion(const Rect<int>& inputRegion)
721 {
722 }
723
724 void WindowBaseCocoa::ExcludeInputRegion(const Rect<int>& inputRegion)
725 {
726 }
727
728 bool WindowBaseCocoa::PointerConstraintsLock()
729 {
730   return false;
731 }
732
733 bool WindowBaseCocoa::PointerConstraintsUnlock()
734 {
735   return false;
736 }
737
738 void WindowBaseCocoa::LockedPointerRegionSet(int32_t x, int32_t y, int32_t width, int32_t height)
739 {
740 }
741
742 void WindowBaseCocoa::LockedPointerCursorPositionHintSet(int32_t x, int32_t y)
743 {
744 }
745
746 bool WindowBaseCocoa::PointerWarp(int32_t x, int32_t y)
747 {
748   return false;
749 }
750
751 void WindowBaseCocoa::CursorVisibleSet(bool visible)
752 {
753 }
754
755 bool WindowBaseCocoa::KeyboardGrab(Device::Subclass::Type deviceSubclass)
756 {
757   return false;
758 }
759
760 bool WindowBaseCocoa::KeyboardUnGrab()
761 {
762   return false;
763 }
764
765 void WindowBaseCocoa::SetFullScreen(bool fullscreen)
766 {
767   return;
768 }
769
770 bool WindowBaseCocoa::GetFullScreen()
771 {
772   return false;
773 }
774
775 } // namespace Dali::Internal::Adaptor
776
777 @implementation CocoaView
778 {
779   WindowBaseCocoa::Impl *mImpl;
780 }
781
782 - (CocoaView *) initWithFrame:(NSRect) rect withImpl:(WindowBaseCocoa::Impl *) impl
783 {
784   self = [super initWithFrame:rect];
785   if (self)
786   {
787     mImpl = impl;
788     self.wantsLayer = YES;
789     self.wantsBestResolutionOpenGLSurface = NO;
790   }
791
792   return self;
793 }
794
795 - (BOOL) isFlipped
796 {
797   return YES;
798 }
799
800 - (BOOL) wantsUpdateLayer
801 {
802   return YES;
803 }
804
805 - (BOOL) acceptsFirstResponder
806 {
807   return YES;
808 }
809
810 - (void) mouseDown:(NSEvent *) event
811 {
812   mImpl->OnMouse(event, Dali::PointState::DOWN);
813 }
814
815 - (void) mouseUp:(NSEvent *) event
816 {
817   mImpl->OnMouse(event, Dali::PointState::UP);
818 }
819
820 - (void) mouseDragged:(NSEvent *) event
821 {
822   mImpl->OnMouse(event, Dali::PointState::MOTION);
823 }
824
825 - (void) keyDown:(NSEvent *) event
826 {
827   mImpl->OnKey(event, Dali::Integration::KeyEvent::DOWN);
828 }
829
830 - (void) keyUp:(NSEvent *) event
831 {
832   mImpl->OnKey(event, Dali::Integration::KeyEvent::UP);
833 }
834
835 - (void) drawRect:(NSRect) dirtyRect
836 {
837   DALI_LOG_INFO(
838     Dali::Internal::Adaptor::gWindowBaseLogFilter,
839     Debug::Verbose,
840     "-[CocoaView drawRect:(%.1f, %.1f, %.1f, %.1f)]\n",
841     dirtyRect.origin.x,
842     dirtyRect.origin.y,
843     dirtyRect.size.width,
844     dirtyRect.size.height
845   );
846
847   mImpl->OnWindowDamaged(dirtyRect);
848 }
849
850 - (void) prepareOpenGL
851 {
852   auto ctx = CGLGetCurrentContext();
853   DALI_ASSERT_ALWAYS(ctx);
854
855   // Enable multithreading
856   if (auto err = CGLEnable(ctx, kCGLCEMPEngine); err != kCGLNoError)
857   {
858     DALI_LOG_ERROR("%s - %s", __PRETTY_FUNCTION__, CGLErrorString(err));
859   }
860 }
861 @end
862
863 @implementation WindowDelegate
864 {
865   WindowBaseCocoa::Impl *mImpl;
866 }
867
868 - (WindowDelegate *) init:(Dali::Internal::Adaptor::WindowBaseCocoa::Impl *) impl
869 {
870   self = [super init];
871   if (self)
872   {
873     mImpl = impl;
874   }
875   return self;
876 }
877
878 - (void) windowDidBecomeKey:(NSNotification *) notification
879 {
880   mImpl->OnFocus(true);
881 }
882
883 - (void) windowDidResignKey:(NSNotification *) notification
884 {
885   mImpl->OnFocus(false);
886 }
887
888 - (void) windowWillClose:(NSNotification *) notification
889 {
890   [NSApp stop:nil];
891 }
892 @end