1 module cocoatest;
2 version(OSX):
3 
4 import derelict.cocoa;
5 
6 import dlangui.core.logger;
7 import dlangui.core.types;
8 import dlangui.core.events;
9 import dlangui.graphics.drawbuf;
10 import std.uuid;
11 import core.stdc.stdlib;
12 import std.string;
13 
14 void main(string[] args)
15 {
16     Log.setStderrLogger();
17     Log.setLogLevel(LogLevel.Trace);
18     DerelictCocoa.load();
19 
20 
21 
22     static if (true) {
23         auto pool = new NSAutoreleasePool;
24         NSString appName = NSProcessInfo.processInfo().processName();
25         Log.i("appName = %s", appName);
26 
27         CocoaWindow window = new CocoaWindow(cast(void*)null, new IWindowListenerLogger(), 300, 300);
28         Log.d("");
29     } else {
30 
31 
32         NSString appName = NSProcessInfo.processInfo().processName();
33         Log.i("appName = %s", appName);
34         //writefln("appName = %s", appName);
35 
36         auto pool = new NSAutoreleasePool;
37 
38         auto NSApp = NSApplication.sharedApplication;
39 
40         NSApp.setActivationPolicy(NSApplicationActivationPolicyRegular);
41 
42         NSMenu menubar = NSMenu.alloc;
43         menubar.init_();
44         NSMenuItem appMenuItem = NSMenuItem.alloc();
45         appMenuItem.init_();
46         menubar.addItem(appMenuItem);
47         NSApp.setMainMenu(menubar);
48 
49         NSWindow window = NSWindow.alloc();
50         window.initWithContentRect(NSMakeRect(10, 10, 200, 200),
51             NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask, //NSBorderlessWindowMask,
52             NSBackingStoreBuffered, NO);
53         window.makeKeyAndOrderFront();
54 
55         NSView parentView;
56         parentView = window.contentView();
57 
58         Log.i("parentView=", parentView);
59 
60         NSApp.activateIgnoringOtherApps(YES);
61 
62     //    string uuid = randomUUID().toString();
63     //    DlanguiCocoaView.customClassName = "DlanguiCocoaView_" ~ uuid;
64     //    DlanguiCocoaView.registerSubclass();
65     //
66     //    _view = DlanguiCocoaView.alloc();
67     //    _view.initialize(this, width, height);
68     //
69     //    parentView.addSubview(_view);
70 
71 
72         NSApp.run();
73     }
74 
75     DerelictCocoa.unload();
76 }
77 
78 interface IWindowListener {
79     void onMouseWheel(int x, int y, int deltaX, int deltaY, MouseState state);
80     void onKeyDown(uint key);
81     void onKeyUp(uint key);
82     void onMouseMove(int x, int y, int deltaX, int deltaY,
83         MouseState mouseState);
84     void onMouseRelease(int x, int y, MouseButton mb, MouseState mouseState);
85     void onMouseClick(int x, int y, MouseButton mb, bool isDoubleClick, MouseState mouseState);
86     void recomputeDirtyAreas();
87     void onResized(int width, int height);
88     void onAnimate(double dt, double time);
89     Rect getDirtyRectangle();
90 }
91 
92 class IWindowListenerLogger : IWindowListener {
93     override void onMouseWheel(int x, int y, int deltaX, int deltaY, MouseState state) {
94         Log.d("onMouseWheel");
95     }
96     override void onKeyDown(uint key) {
97         Log.d("onKeyDown");
98     }
99     override void onKeyUp(uint key) {
100         Log.d("onKeyUp");
101     }
102     override void onMouseMove(int x, int y, int deltaX, int deltaY,
103         MouseState mouseState) {
104         Log.d("onMouseMove ", x, ", ", y);
105     }
106     override void onMouseRelease(int x, int y, MouseButton mb, MouseState mouseState) {
107         Log.d("onMouseRelease");
108     }
109     override void onMouseClick(int x, int y, MouseButton mb, bool isDoubleClick, MouseState mouseState) {
110         Log.d("onMouseClick");
111     }
112     override void recomputeDirtyAreas() {
113         //Log.d("recomputeDirtyAreas");
114     }
115     override void onResized(int width, int height) {
116         Log.d("onResized ", width, ", ", height);
117         _width = width;
118         _height = height;
119     }
120     override void onAnimate(double dt, double time) {
121         //Log.d("onAnimate");
122     }
123     override Rect getDirtyRectangle() {
124         return Rect(0, 0, _width, _height);
125     }
126     int _width = 100;
127     int _height = 100;
128 }
129 
130 struct MouseState {
131     bool leftButtonDown;
132     bool rightButtonDown;
133     bool middleButtonDown;
134     bool ctrlPressed;
135     bool shiftPressed;
136     bool altPressed;
137 }
138 
139 enum MouseButton : int {
140     left,
141     right,
142     middle
143 }
144 
145 final class CocoaWindow
146 {
147 private:
148     IWindowListener _listener;
149 
150     // Stays null in the case of a plugin, but exists for a stand-alone program
151     // For testing purpose.
152     NSWindow _cocoaWindow = null;
153     NSApplication _cocoaApplication;
154 
155     NSColorSpace _nsColorSpace;
156     CGColorSpaceRef _cgColorSpaceRef;
157     NSData _imageData;
158     NSString _logFormatStr;
159 
160     ColorDrawBuf _drawBuf;
161 
162     DPlugCustomView _view = null;
163 
164     bool _terminated = false;
165 
166     int _lastMouseX, _lastMouseY;
167     bool _firstMouseMove = true;
168 
169     int _width;
170     int _height;
171 
172     ubyte* _buffer = null;
173 
174     uint _timeAtCreationInMs;
175     uint _lastMeasturedTimeInMs;
176     bool _dirtyAreasAreNotYetComputed;
177 
178 public:
179 
180     this(void* parentWindow, IWindowListener listener, int width, int height)
181     {
182         _listener = listener;
183 
184         DerelictCocoa.load();
185         NSApplicationLoad(); // to use Cocoa in Carbon applications
186         bool parentViewExists = parentWindow !is null;
187         NSView parentView;
188         if (!parentViewExists)
189         {
190             // create a NSWindow to hold our NSView
191             _cocoaApplication = NSApplication.sharedApplication;
192             _cocoaApplication.setActivationPolicy(NSApplicationActivationPolicyRegular);
193 
194             NSWindow window = NSWindow.alloc();
195             window.initWithContentRect(NSMakeRect(100, 100, width, height),
196                 NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask,
197                 NSBackingStoreBuffered,
198                 NO);
199             window.makeKeyAndOrderFront();
200 
201             parentView = window.contentView();
202             //parentView.
203 
204             _cocoaApplication.activateIgnoringOtherApps(YES);
205         }
206         else
207             parentView = NSView(cast(id)parentWindow);
208 
209 
210 
211         _width = 0;
212         _height = 0;
213 
214         _nsColorSpace = NSColorSpace.sRGBColorSpace();
215         // hopefully not null else the colors will be brighter
216         _cgColorSpaceRef = _nsColorSpace.CGColorSpace();
217 
218         _logFormatStr = NSString.stringWith("%@");
219 
220         _timeAtCreationInMs = getTimeMs();
221         _lastMeasturedTimeInMs = _timeAtCreationInMs;
222 
223         _dirtyAreasAreNotYetComputed = true;
224 
225         string uuid = randomUUID().toString();
226         DPlugCustomView.customClassName = "DPlugCustomView_" ~ uuid;
227         DPlugCustomView.registerSubclass();
228 
229         _view = DPlugCustomView.alloc();
230         _view.initialize(this, width, height);
231 
232         parentView.addSubview(_view);
233 
234         if (_cocoaApplication)
235             _cocoaApplication.run();
236 
237 
238     }
239 
240     ~this()
241     {
242         if (_view)
243         {
244             //debug ensureNotInGC("CocoaWindow");
245             _terminated = true;
246 
247             {
248                 _view.killTimer();
249             }
250 
251             _view.removeFromSuperview();
252             _view.release();
253             _view = DPlugCustomView(null);
254 
255             DPlugCustomView.unregisterSubclass();
256 
257             if (_buffer != null)
258             {
259                 free(_buffer);
260                 _buffer = null;
261             }
262 
263             DerelictCocoa.unload();
264         }
265     }
266 
267     // Implements IWindow
268     void waitEventAndDispatch()
269     {
270         assert(false); // not implemented in Cocoa, since we don't have a NSWindow
271     }
272 
273     bool terminated()
274     {
275         return _terminated;
276     }
277 
278     void debugOutput(string s)
279     {
280         import core.stdc.stdio;
281         fprintf(stderr, toStringz(s));
282     }
283 
284     uint getTimeMs()
285     {
286         return cast(uint)(NSDate.timeIntervalSinceReferenceDate() * 1000.0);
287     }
288 
289 private:
290 
291     MouseState getMouseState(NSEvent event)
292     {
293         // not working
294         MouseState state;
295         uint pressedMouseButtons = event.pressedMouseButtons();
296         if (pressedMouseButtons & 1)
297             state.leftButtonDown = true;
298         if (pressedMouseButtons & 2)
299             state.rightButtonDown = true;
300         if (pressedMouseButtons & 4)
301             state.middleButtonDown = true;
302 
303         NSEventModifierFlags mod = event.modifierFlags();
304         if (mod & NSControlKeyMask)
305             state.ctrlPressed = true;
306         if (mod & NSShiftKeyMask)
307             state.shiftPressed = true;
308         if (mod & NSAlternateKeyMask)
309             state.altPressed = true;
310 
311         return state;
312     }
313 
314     void handleMouseWheel(NSEvent event)
315     {
316         int deltaX = cast(int)(0.5 + 10 * event.deltaX);
317         int deltaY = cast(int)(0.5 + 10 * event.deltaY);
318         Point mousePos = getMouseXY(_view, event, _height);
319         _listener.onMouseWheel(mousePos.x, mousePos.y, deltaX, deltaY, getMouseState(event));
320     }
321 
322     void handleKeyEvent(NSEvent event, bool released)
323     {
324         uint keyCode = event.keyCode();
325         uint key;
326         switch (keyCode)
327         {
328             case kVK_ANSI_Keypad0: key = KeyCode.KEY_0; break;
329             case kVK_ANSI_Keypad1: key = KeyCode.KEY_1; break;
330             case kVK_ANSI_Keypad2: key = KeyCode.KEY_2; break;
331             case kVK_ANSI_Keypad3: key = KeyCode.KEY_3; break;
332             case kVK_ANSI_Keypad4: key = KeyCode.KEY_4; break;
333             case kVK_ANSI_Keypad5: key = KeyCode.KEY_5; break;
334             case kVK_ANSI_Keypad6: key = KeyCode.KEY_6; break;
335             case kVK_ANSI_Keypad7: key = KeyCode.KEY_7; break;
336             case kVK_ANSI_Keypad8: key = KeyCode.KEY_8; break;
337             case kVK_ANSI_Keypad9: key = KeyCode.KEY_9; break;
338             case kVK_Return: key = KeyCode.RETURN; break;
339             case kVK_Escape: key = KeyCode.ESCAPE; break;
340             case kVK_LeftArrow: key = KeyCode.LEFT; break;
341             case kVK_RightArrow: key = KeyCode.RIGHT; break;
342             case kVK_DownArrow: key = KeyCode.DOWN; break;
343             case kVK_UpArrow: key = KeyCode.UP; break;
344             default: key = 0;
345         }
346 
347         if (released)
348             _listener.onKeyDown(key);
349         else
350             _listener.onKeyUp(key);
351     }
352 
353     void handleMouseMove(NSEvent event)
354     {
355         Point mousePos = getMouseXY(_view, event, _height);
356 
357         if (_firstMouseMove)
358         {
359             _firstMouseMove = false;
360             _lastMouseX = mousePos.x;
361             _lastMouseY = mousePos.y;
362         }
363 
364         _listener.onMouseMove(mousePos.x, mousePos.y, mousePos.x - _lastMouseX, mousePos.y - _lastMouseY,
365             getMouseState(event));
366 
367         _lastMouseX = mousePos.x;
368         _lastMouseY = mousePos.y;
369     }
370 
371     void handleMouseClicks(NSEvent event, MouseButton mb, bool released)
372     {
373         Point mousePos = getMouseXY(_view, event, _height);
374 
375         if (released)
376             _listener.onMouseRelease(mousePos.x, mousePos.y, mb, getMouseState(event));
377         else
378         {
379             int clickCount = event.clickCount();
380             bool isDoubleClick = clickCount >= 2;
381             _listener.onMouseClick(mousePos.x, mousePos.y, mb, isDoubleClick, getMouseState(event));
382         }
383     }
384 
385     enum scanLineAlignment = 4; // could be anything
386 
387     // given a width, how long in bytes should scanlines be
388     int byteStride(int width)
389     {
390         int widthInBytes = width * 4;
391         return (widthInBytes + (scanLineAlignment - 1)) & ~(scanLineAlignment-1);
392     }
393 
394     void drawRect(NSRect rect)
395     {
396         NSGraphicsContext nsContext = NSGraphicsContext.currentContext();
397 
398         CIContext ciContext = nsContext.getCIContext();
399 
400         // update internal buffers in case of startup/resize
401         {
402             NSRect boundsRect = _view.bounds();
403             int width = cast(int)(boundsRect.size.width);   // truncating down the dimensions of bounds
404             int height = cast(int)(boundsRect.size.height);
405             updateSizeIfNeeded(width, height);
406             _drawBuf.resize(width, height);
407         }
408 
409         // The first drawRect callback occurs before the timer triggers.
410         // But because recomputeDirtyAreas() wasn't called before there is nothing to draw.
411         // Hence, do it.
412         if (_dirtyAreasAreNotYetComputed)
413         {
414             _dirtyAreasAreNotYetComputed = false;
415             _listener.recomputeDirtyAreas();
416         }
417 
418         // draw buffers
419 //        ImageRef!RGBA wfb;
420 //        wfb.w = _width;
421 //        wfb.h = _height;
422 //        wfb.pitch = byteStride(_width);
423 //        wfb.pixels = cast(RGBA*)_buffer;
424 //        _listener.onDraw(wfb, WindowPixelFormat.ARGB8);
425         _drawBuf.fill(0x8090B0);
426         _drawBuf.fillRect(Rect(20, 20, 120, 120), 0xFFBBBB);
427 
428         size_t sizeNeeded = byteStride(_width) * _height;
429         //_imageData = NSData.dataWithBytesNoCopy(cast(ubyte*)_drawBuf.scanLine(0), sizeNeeded, false);
430         _imageData = NSData.dataWithBytesNoCopy(cast(ubyte*)_drawBuf.scanLine(0), sizeNeeded, false);
431 
432         CIImage image = CIImage.imageWithBitmapData(_imageData,
433             byteStride(_drawBuf.width),
434             CGSize(_drawBuf.width, _drawBuf.height),
435             kCIFormatARGB8,
436             _cgColorSpaceRef);
437 //        NSRect rc;
438 //        rc.origin.x = 0;
439 //        rc.origin.y = 0;
440 //        rc.size.width = _drawBuf.width;
441 //        rc.size.height = _drawBuf.height;
442         ciContext.drawImage(image, rect, rect);
443     }
444 
445     /// Returns: true if window size changed.
446     bool updateSizeIfNeeded(int newWidth, int newHeight)
447     {
448         // only do something if the client size has changed
449         if ( (newWidth != _width) || (newHeight != _height) )
450         {
451             // Extends buffer
452             if (_buffer != null)
453             {
454                 free(_buffer);
455                 _buffer = null;
456             }
457 
458             size_t sizeNeeded = byteStride(newWidth) * newHeight;
459             _buffer = cast(ubyte*) malloc(sizeNeeded);
460             _width = newWidth;
461             _height = newHeight;
462             if (_drawBuf is null)
463                 _drawBuf = new ColorDrawBuf(_width, _height);
464             else if (_drawBuf.width != _width || _drawBuf.height != _height)
465                 _drawBuf.resize(_width, _height);
466             _listener.onResized(_width, _height);
467             return true;
468         }
469         else
470             return false;
471     }
472 
473     void doAnimation()
474     {
475         uint now = getTimeMs();
476         double dt = (now - _lastMeasturedTimeInMs) * 0.001;
477         double time = (now - _timeAtCreationInMs) * 0.001; // hopefully no plug-in will be open more than 49 days
478         _lastMeasturedTimeInMs = now;
479         _listener.onAnimate(dt, time);
480     }
481 
482     void onTimer()
483     {
484         // Deal with animation
485         doAnimation();
486 
487         _listener.recomputeDirtyAreas();
488         _dirtyAreasAreNotYetComputed = false;
489         Rect dirtyRect = _listener.getDirtyRectangle();
490         if (!dirtyRect.empty())
491         {
492 
493             NSRect boundsRect = _view.bounds();
494             int height = cast(int)(boundsRect.size.height);
495             NSRect r = NSMakeRect(dirtyRect.left,
496                 height - dirtyRect.top - dirtyRect.height,
497                 dirtyRect.width,
498                 dirtyRect.height);
499             _view.setNeedsDisplayInRect(r);
500         }
501     }
502 }
503 
504 struct DPlugCustomView
505 {
506     // This class uses a unique class name for each plugin instance
507     static string customClassName = null;
508 
509     NSView parent;
510     alias parent this;
511 
512     // create from an id
513     this (id id_)
514     {
515         this._id = id_;
516     }
517 
518     /// Allocates, but do not init
519     static DPlugCustomView alloc()
520     {
521         alias fun_t = extern(C) id function (id obj, SEL sel);
522         return DPlugCustomView( (cast(fun_t)objc_msgSend)(getClassID(), sel!"alloc") );
523     }
524 
525     static Class getClass()
526     {
527         return cast(Class)( getClassID() );
528     }
529 
530     static id getClassID()
531     {
532         assert(customClassName !is null);
533         return objc_getClass(customClassName);
534     }
535 
536 private:
537 
538     CocoaWindow _window;
539     NSTimer _timer = null;
540 
541     void initialize(CocoaWindow window, int width, int height)
542     {
543         // Warning: taking this address is fishy since DPlugCustomView is a struct and thus could be copied
544         // we rely on the fact it won't :|
545         void* thisPointer = cast(void*)(&this);
546         object_setInstanceVariable(_id, "this", thisPointer);
547 
548         this._window = window;
549 
550         NSRect r = NSRect(NSPoint(0, 0), NSSize(width, height));
551         initWithFrame(r);
552 
553         _timer = NSTimer.timerWithTimeInterval(1 / 60.0, this, sel!"onTimer:", null, true);
554         NSRunLoop.currentRunLoop().addTimer(_timer, NSRunLoopCommonModes);
555     }
556 
557     static Class clazz;
558 
559     static void registerSubclass()
560     {
561         clazz = objc_allocateClassPair(cast(Class) lazyClass!"NSView", toStringz(customClassName), 0);
562 
563         class_addMethod(clazz, sel!"keyDown:", cast(IMP) &keyDown, "v@:@");
564         class_addMethod(clazz, sel!"keyUp:", cast(IMP) &keyUp, "v@:@");
565         class_addMethod(clazz, sel!"mouseDown:", cast(IMP) &mouseDown, "v@:@");
566         class_addMethod(clazz, sel!"mouseUp:", cast(IMP) &mouseUp, "v@:@");
567         class_addMethod(clazz, sel!"rightMouseDown:", cast(IMP) &rightMouseDown, "v@:@");
568         class_addMethod(clazz, sel!"rightMouseUp:", cast(IMP) &rightMouseUp, "v@:@");
569         class_addMethod(clazz, sel!"otherMouseDown:", cast(IMP) &otherMouseDown, "v@:@");
570         class_addMethod(clazz, sel!"otherMouseUp:", cast(IMP) &otherMouseUp, "v@:@");
571         class_addMethod(clazz, sel!"mouseMoved:", cast(IMP) &mouseMoved, "v@:@");
572         class_addMethod(clazz, sel!"mouseDragged:", cast(IMP) &mouseMoved, "v@:@");
573         class_addMethod(clazz, sel!"rightMouseDragged:", cast(IMP) &mouseMoved, "v@:@");
574         class_addMethod(clazz, sel!"otherMouseDragged:", cast(IMP) &mouseMoved, "v@:@");
575         class_addMethod(clazz, sel!"acceptsFirstResponder", cast(IMP) &acceptsFirstResponder, "b@:");
576         class_addMethod(clazz, sel!"isOpaque", cast(IMP) &isOpaque, "b@:");
577         class_addMethod(clazz, sel!"acceptsFirstMouse:", cast(IMP) &acceptsFirstMouse, "b@:@");
578         class_addMethod(clazz, sel!"viewDidMoveToWindow", cast(IMP) &viewDidMoveToWindow, "v@:");
579         class_addMethod(clazz, sel!"drawRect:", cast(IMP) &drawRect, "v@:" ~ encode!NSRect);
580         class_addMethod(clazz, sel!"onTimer:", cast(IMP) &onTimer, "v@:@");
581 
582         // This ~ is to avoid a strange DMD ICE. Didn't succeed in isolating it.
583         class_addMethod(clazz, sel!("scroll" ~ "Wheel:") , cast(IMP) &scrollWheel, "v@:@");
584 
585         // very important: add an instance variable for the this pointer so that the D object can be
586         // retrieved from an id
587         class_addIvar(clazz, "this", (void*).sizeof, (void*).sizeof == 4 ? 2 : 3, "^v");
588 
589         objc_registerClassPair(clazz);
590     }
591 
592     static void unregisterSubclass()
593     {
594         // For some reason the class need to continue to exist, so we leak it
595         //  objc_disposeClassPair(clazz);
596         // TODO: remove this crap
597     }
598 
599     void killTimer()
600     {
601         if (_timer)
602         {
603             _timer.invalidate();
604             _timer = NSTimer(null);
605         }
606     }
607 }
608 
609 DPlugCustomView getInstance(id anId)
610 {
611     // strange thing: object_getInstanceVariable definition is odd (void**)
612     // and only works for pointer-sized values says SO
613     void* thisPointer = null;
614     Ivar var = object_getInstanceVariable(anId, "this", &thisPointer);
615     assert(var !is null);
616     assert(thisPointer !is null);
617     return *cast(DPlugCustomView*)thisPointer;
618 }
619 
620 Point getMouseXY(NSView view, NSEvent event, int windowHeight)
621 {
622     NSPoint mouseLocation = event.locationInWindow();
623     mouseLocation = view.convertPoint(mouseLocation, NSView(null));
624     int px = cast(int)(mouseLocation.x) - 2;
625     int py = windowHeight - cast(int)(mouseLocation.y) - 3;
626     return Point(px, py);
627 }
628 
629 // Overridden function gets called with an id, instead of the self pointer.
630 // So we have to get back the D class object address.
631 // Big thanks to Mike Ash (@macdev)
632 extern(C)
633 {
634     void keyDown(id self, SEL selector, id event)
635     {
636         //FPControl fpctrl;
637         //fpctrl.initialize();
638         DPlugCustomView view = getInstance(self);
639         view._window.handleKeyEvent(NSEvent(event), false);
640     }
641 
642     void keyUp(id self, SEL selector, id event)
643     {
644         //FPControl fpctrl;
645         //fpctrl.initialize();
646         DPlugCustomView view = getInstance(self);
647         view._window.handleKeyEvent(NSEvent(event), true);
648     }
649 
650     void mouseDown(id self, SEL selector, id event)
651     {
652         //FPControl fpctrl;
653         //fpctrl.initialize();
654         DPlugCustomView view = getInstance(self);
655         view._window.handleMouseClicks(NSEvent(event), MouseButton.left, false);
656     }
657 
658     void mouseUp(id self, SEL selector, id event)
659     {
660         //FPControl fpctrl;
661         //fpctrl.initialize();
662         DPlugCustomView view = getInstance(self);
663         view._window.handleMouseClicks(NSEvent(event), MouseButton.left, true);
664     }
665 
666     void rightMouseDown(id self, SEL selector, id event)
667     {
668         //FPControl fpctrl;
669         //fpctrl.initialize();
670         DPlugCustomView view = getInstance(self);
671         view._window.handleMouseClicks(NSEvent(event), MouseButton.right, false);
672     }
673 
674     void rightMouseUp(id self, SEL selector, id event)
675     {
676         //FPControl fpctrl;
677         //fpctrl.initialize();
678         DPlugCustomView view = getInstance(self);
679         view._window.handleMouseClicks(NSEvent(event), MouseButton.right, true);
680     }
681 
682     void otherMouseDown(id self, SEL selector, id event)
683     {
684         //FPControl fpctrl;
685         //fpctrl.initialize();
686         DPlugCustomView view = getInstance(self);
687         auto nsEvent = NSEvent(event);
688         if (nsEvent.buttonNumber == 2)
689             view._window.handleMouseClicks(nsEvent, MouseButton.middle, false);
690     }
691 
692     void otherMouseUp(id self, SEL selector, id event)
693     {
694         //FPControl fpctrl;
695         //fpctrl.initialize();
696         DPlugCustomView view = getInstance(self);
697         auto nsEvent = NSEvent(event);
698         if (nsEvent.buttonNumber == 2)
699             view._window.handleMouseClicks(nsEvent, MouseButton.middle, true);
700     }
701 
702     void mouseMoved(id self, SEL selector, id event)
703     {
704         //FPControl fpctrl;
705         //fpctrl.initialize();
706         DPlugCustomView view = getInstance(self);
707         view._window.handleMouseMove(NSEvent(event));
708     }
709 
710     void scrollWheel(id self, SEL selector, id event)
711     {
712         //FPControl fpctrl;
713         //fpctrl.initialize();
714         DPlugCustomView view = getInstance(self);
715         view._window.handleMouseWheel(NSEvent(event));
716     }
717 
718     bool acceptsFirstResponder(id self, SEL selector)
719     {
720         return YES;
721     }
722 
723     bool acceptsFirstMouse(id self, SEL selector, id pEvent)
724     {
725         return YES;
726     }
727 
728     bool isOpaque(id self, SEL selector)
729     {
730         return YES;
731     }
732 
733     void viewDidMoveToWindow(id self, SEL selector)
734     {
735         DPlugCustomView view = getInstance(self);
736         NSWindow parentWindow = view.window();
737         if (parentWindow)
738         {
739             parentWindow.makeFirstResponder(view);
740             parentWindow.setAcceptsMouseMovedEvents(true);
741         }
742     }
743 
744     void drawRect(id self, SEL selector, NSRect rect)
745     {
746         //FPControl fpctrl;
747         //fpctrl.initialize();
748         DPlugCustomView view = getInstance(self);
749         view._window.drawRect(rect);
750     }
751 
752     void onTimer(id self, SEL selector, id timer)
753     {
754         //FPControl fpctrl;
755         //fpctrl.initialize();
756         DPlugCustomView view = getInstance(self);
757         view._window.onTimer();
758     }
759 }