1 module fluid.backend.simpledisplay;
2 
3 version (Have_arsd_official_simpledisplay):
4 
5 debug (Fluid_BuildMessages) {
6     pragma(msg, "Fluid: Building with arsd.simpledisplay support");
7 }
8 
9 import arsd.simpledisplay;
10 
11 import std.algorithm;
12 import std.datetime.stopwatch;
13 
14 import fluid.backend;
15 
16 
17 @safe:
18 
19 
20 private {
21     alias Rectangle = fluid.backend.Rectangle;
22     alias Color = fluid.backend.Color;
23     alias Image = fluid.backend.Image;
24     alias MouseButton = fluid.backend.MouseButton;
25 }
26 
27 class SimpledisplayBackend : FluidBackend {
28 
29     SimpleWindow window;
30 
31     private enum InputState {
32         up,
33         pressed,
34         down,
35         released,
36         repeated,
37     }
38 
39     private {
40 
41         Vector2 _mousePosition;
42         int _dpi;
43         float _scale = 1;
44         bool _hasJustResized;
45         StopWatch _stopWatch;
46         float _deltaTime;
47         Rectangle _scissors;
48         bool _scissorsEnabled;
49         FluidMouseCursor _cursor;
50 
51         TextureReaper _reaper;
52 
53         /// Recent input events for keyboard and mouse.
54         InputState[KeyboardKey.max+1] _keyboardState;
55         InputState[MouseButton.max+1] _mouseState;
56         int _scroll;
57         // gamepads?
58 
59         /// Characters typed by the user, awaiting consumption.
60         const(dchar)[] _characterQueue;
61 
62         // Missing from simpledisplay at the time of writing
63         extern(C) void function(GLint x, GLint y, GLsizei width, GLsizei height) glScissor;
64 
65     }
66 
67     // TODO non-openGL backend, maybe...
68 
69     /// Initialize the backend using the given window.
70     ///
71     /// Make sure to call `SimpledisplayBackend.poll()` *after* the Fluid `draw` call, and only do it once per frame,
72     /// other Fluid might not be able to keep itself up to date with latest events.
73     ///
74     /// Please note Fluid will register its own event handlers, so if you
75     /// intend to use them, you should make sure to call whatever value was set previously.
76     ///
77     /// ---
78     /// auto oldMouseHandler = window.handleMouseEvent;
79     /// window.handleMouseEvent = (MouseEvent event) {
80     ///     oldMouseHandler(event);
81     ///     // ... do your stuff ...
82     /// };
83     /// ---
84     ///
85     /// Gamepad input is not supported for simpledisplay.
86     this(SimpleWindow window) {
87 
88         this.window = window;
89 
90         () @trusted {
91             this.glScissor = cast(typeof(glScissor)) glbindGetProcAddress("glScissor");
92         }();
93 
94         updateDPI();
95         _stopWatch.start();
96 
97         auto oldMouseHandler = window.handleMouseEvent;
98         auto oldKeyHandler = window.handleKeyEvent;
99         auto oldCharHandler = window.handleCharEvent;
100         auto oldWindowResized = window.windowResized;
101         auto oldOnDpiChanged = window.onDpiChanged;
102 
103         // Register a mouse handler
104         this.window.handleMouseEvent = (MouseEvent event) {
105 
106             if (oldMouseHandler) oldMouseHandler(event);
107 
108             // Scroll event
109             if (const scroll = event.button.scrollValue) {
110 
111                 if (event.type == event.type.buttonPressed) {
112 
113                     _scroll = scroll;
114 
115                 }
116 
117             }
118 
119             // Other events
120             else final switch (event.type) {
121 
122                 // Update mouse position
123                 case event.type.motion:
124                     _mousePosition = Vector2(event.x, event.y);
125                     return;
126 
127                 // Update button state
128                 case event.type.buttonPressed:
129                     _mouseState[event.button.toFluid] = InputState.pressed;
130                     return;
131 
132                 case event.type.buttonReleased:
133                     _mouseState[event.button.toFluid] = InputState.released;
134                     return;
135 
136             }
137 
138         };
139 
140         // Register a keyboard handler
141         this.window.handleKeyEvent = (KeyEvent event) {
142 
143             if (oldKeyHandler) oldKeyHandler(event);
144 
145             const key = event.key.toFluid;
146 
147             // Released
148             if (!event.pressed)
149                 _keyboardState[key] = InputState.released;
150 
151             // Repeat
152             else if (isDown(key))
153                 _keyboardState[key] = InputState.repeated;
154 
155             // Pressed
156             else
157                 _keyboardState[key] = InputState.pressed;
158 
159         };
160 
161         // Register character handler
162         this.window.handleCharEvent = (dchar character) {
163 
164             import std.uni;
165 
166             if (oldCharHandler) oldCharHandler(character);
167 
168             // Ignore control characters
169             if (character.isControl) return;
170 
171             // Send new characters
172             _characterQueue ~= character;
173 
174         };
175 
176         // Register a resize handler
177         this.window.windowResized = (int width, int height) {
178 
179             if (oldWindowResized) oldWindowResized(width, height);
180 
181             // Update window size
182             _hasJustResized = true;
183             glViewport(0, 0, width, height);
184 
185         };
186 
187         this.window.onDpiChanged = () {
188 
189             if (oldOnDpiChanged) oldOnDpiChanged();
190 
191             // Update window size
192             _hasJustResized = true;
193             updateDPI();
194 
195         };
196 
197     }
198 
199     bool isPressed(MouseButton button) const {
200 
201         return _mouseState[button] == InputState.pressed;
202 
203     }
204 
205     bool isReleased(MouseButton button) const {
206 
207         return _mouseState[button] == InputState.released;
208 
209     }
210 
211     bool isDown(MouseButton button) const {
212 
213         return _mouseState[button].among(InputState.pressed, InputState.down) != 0;
214 
215     }
216 
217     bool isUp(MouseButton button) const {
218 
219         return _mouseState[button].among(InputState.released, InputState.up) != 0;
220 
221     }
222 
223     bool isPressed(KeyboardKey key) const {
224 
225         return _keyboardState[key] == InputState.pressed;
226 
227     }
228 
229     bool isReleased(KeyboardKey key) const {
230 
231         return _keyboardState[key] == InputState.released;
232 
233     }
234 
235     bool isDown(KeyboardKey key) const {
236 
237         return _keyboardState[key].among(InputState.pressed, InputState.repeated, InputState.down) != 0;
238 
239     }
240 
241     bool isUp(KeyboardKey key) const {
242 
243         return _keyboardState[key].among(InputState.released, InputState.up) != 0;
244 
245     }
246 
247     bool isRepeated(KeyboardKey key) const {
248 
249         return _keyboardState[key] == InputState.repeated;
250 
251     }
252 
253 
254     dchar inputCharacter() {
255 
256         // No characters in queue
257         if (_characterQueue.length == 0)
258             return '\0';
259 
260         // Pop the first character
261         auto result = _characterQueue[0];
262         _characterQueue = _characterQueue[1..$];
263         return result;
264 
265     }
266 
267     int isPressed(GamepadButton button) const
268         => 0;
269     int isReleased(GamepadButton button) const
270         => 0;
271     int isDown(GamepadButton button) const
272         => 0;
273     int isUp(GamepadButton button) const
274         => 1;
275     int isRepeated(GamepadButton button) const
276         => 0;
277 
278     private void updateDPI() @trusted {
279 
280         _dpi = either(window.actualDpi, 96);
281 
282     }
283 
284     /// Update event state. To be called *after* drawing.
285     void poll() {
286 
287         // Calculate delta time
288         _deltaTime = _stopWatch.peek.total!"msecs" / 1000f;
289         _stopWatch.reset();
290 
291         // Reset frame state
292         _hasJustResized = false;
293         _characterQueue = null;
294         _scroll = 0;
295 
296         foreach (ref state; _keyboardState) {
297             if (state == state.pressed) state = state.down;
298             if (state == state.repeated) state = state.down;
299             if (state == state.released) state = state.up;
300         }
301 
302         foreach (ref state; _mouseState) {
303             if (state == state.pressed) state = state.down;
304             if (state == state.released) state = state.up;
305         }
306 
307     }
308 
309     Vector2 mousePosition(Vector2 position) @trusted {
310 
311         auto positionRay = toSdpyCoords(position);
312         window.warpMouse(cast(int) positionRay.x, cast(int) positionRay.y);
313         return _mousePosition = position;
314 
315     }
316 
317     Vector2 mousePosition() const {
318 
319         return toFluidCoords(_mousePosition);
320 
321     }
322 
323     Vector2 scroll() const {
324 
325         return Vector2(0, _scroll);
326 
327     }
328 
329     float deltaTime() const {
330 
331         return _deltaTime;
332 
333     }
334 
335     bool hasJustResized() const {
336 
337         return _hasJustResized;
338 
339     }
340 
341     Vector2 windowSize(Vector2 size) @trusted {
342 
343         auto sizeRay = toSdpyCoords(size);
344         window.resize(cast(int) sizeRay.x, cast(int) sizeRay.y);
345         return size;
346 
347     }
348 
349     Vector2 windowSize() const {
350 
351         return toFluidCoords(Vector2(window.width, window.height));
352 
353     }
354 
355     /// Convert window coordinates to OpenGL coordinates; done *after* toSdpyCoords.
356     Vector2 toGL(Vector2 coords) {
357 
358         return Vector2(
359             coords.x / window.width * 2 - 1,
360             1 - coords.y / window.height * 2
361         );
362 
363     }
364 
365     /// Create a vertex at given screenspace position
366     void vertex(Vector2 coords) @trusted {
367 
368         glVertex2f(toGL(coords).tupleof);
369 
370     }
371 
372     float scale() const {
373 
374         return _scale;
375 
376     }
377 
378     float scale(float value) {
379 
380         return _scale = value;
381 
382     }
383 
384     Vector2 dpi() const @trusted {
385 
386         return Vector2(_dpi, _dpi) * scale;
387 
388     }
389 
390     Vector2 toSdpyCoords(Vector2 position) const @trusted {
391 
392         return Vector2(position.x * hidpiScale.x, position.y * hidpiScale.y);
393 
394     }
395 
396     Rectangle toSdpyCoords(Rectangle rec) const @trusted {
397 
398         return Rectangle(
399             rec.x * hidpiScale.x,
400             rec.y * hidpiScale.y,
401             rec.width * hidpiScale.x,
402             rec.height * hidpiScale.y,
403         );
404 
405     }
406 
407     Vector2 toFluidCoords(Vector2 position) const @trusted {
408 
409         return Vector2(position.x / hidpiScale.x, position.y / hidpiScale.y);
410 
411     }
412 
413     Vector2 toFluidCoords(float x, float y) const @trusted {
414 
415         return Vector2(x / hidpiScale.x, y / hidpiScale.y);
416 
417     }
418 
419     Rectangle toFluidCoords(Rectangle rec) const @trusted {
420 
421         return Rectangle(
422             rec.x / hidpiScale.x,
423             rec.y / hidpiScale.y,
424             rec.width / hidpiScale.x,
425             rec.height / hidpiScale.y,
426         );
427 
428     }
429 
430     Rectangle area(Rectangle rect) @trusted {
431 
432         auto rectRay = toSdpyCoords(rect);
433 
434         glEnable(GL_SCISSOR_TEST);
435         glScissor(
436             cast(int) rectRay.x,
437             cast(int) (window.height - rectRay.y - rectRay.height),
438             cast(int) rectRay.width,
439             cast(int) rectRay.height,
440         );
441         _scissorsEnabled = true;
442 
443         return _scissors = rect;
444 
445     }
446 
447     Rectangle area() const {
448 
449         if (_scissorsEnabled)
450             return _scissors;
451         else
452             return Rectangle(0, 0, windowSize.tupleof);
453 
454     }
455 
456     void restoreArea() @trusted {
457 
458         glDisable(GL_SCISSOR_TEST);
459         _scissorsEnabled = false;
460 
461     }
462 
463     FluidMouseCursor mouseCursor(FluidMouseCursor cursor) @trusted {
464 
465         // Hide the cursor
466         if (cursor.system == cursor.system.none) {
467             window.hideCursor();
468         }
469 
470         // Show the cursor
471         else {
472             window.showCursor();
473             window.cursor = cursor.toSimpleDisplay;
474         }
475 
476         return _cursor = cursor;
477 
478     }
479 
480     FluidMouseCursor mouseCursor() const {
481 
482         return _cursor;
483 
484     }
485 
486     TextureReaper* reaper() return scope {
487 
488         return &_reaper;
489 
490     }
491 
492     Texture loadTexture(Image image) @system {
493 
494         Texture result;
495         result.width = image.width;
496         result.height = image.height;
497 
498         // Create an OpenGL texture
499         glGenTextures(1, &result.id);
500         glBindTexture(GL_TEXTURE_2D, result.id);
501 
502         // Prepare the tombstone
503         result.tombstone = reaper.makeTombstone(this, result.id);
504 
505         // No filtering
506         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
507         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
508 
509         // Repeat on
510         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
511         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
512 
513         // Upload the data
514         glTexImage2D(
515 
516             // 2D texture, no mipmaps, four channels
517             GL_TEXTURE_2D, 0, GL_RGBA,
518 
519             // Size
520             image.width, image.height,
521 
522             // No border
523             0,
524 
525             // Formatted as R8B8G8A8
526             GL_RGBA, GL_UNSIGNED_BYTE, image.pixels.ptr,
527 
528         );
529 
530         // Unbind the texture
531         glBindTexture(GL_TEXTURE_2D, 0);
532 
533         return result;
534 
535     }
536 
537     Image loadImage(string filename) @system {
538 
539         version (Have_arsd_official_image_files) {
540 
541             import arsd.image;
542 
543             // Load the image
544             auto image = loadImageFromFile(filename).getAsTrueColorImage;
545 
546             // Convert to a Fluid image
547             Image result;
548             result.pixels = cast(Color[]) image.imageData.bytes;
549             result.width = image.width;
550             result.height = image.height;
551             return result;
552 
553         }
554 
555         else assert(false, "arsd-official:image_files is required to load images from files");
556 
557     }
558 
559     Texture loadTexture(string filename) @system {
560 
561         return loadTexture(loadImage(filename));
562 
563     }
564 
565     /// Destroy a texture
566     void unloadTexture(uint id) @system {
567 
568         if (id == 0) return;
569 
570         glDeleteTextures(1, &id);
571 
572     }
573 
574     private void openglDraw() @trusted {
575 
576         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
577         glEnable(GL_BLEND);
578         glLoadIdentity();
579 
580         // This must be present, otherwise the AMD Linux driver will hang when the window is resized. I don't have the
581         // slightest clue why.
582         if (auto error = glGetError()) {
583             import std.stdio;
584             debug writeln(error);
585         }
586 
587     }
588 
589     void drawLine(Vector2 start, Vector2 end, Color color) @trusted {
590 
591         openglDraw();
592         glBegin(GL_LINES);
593 
594         glColor4ub(color.tupleof);
595         vertex(toSdpyCoords(start));
596         vertex(toSdpyCoords(end));
597 
598         glEnd();
599 
600     }
601 
602     void drawTriangle(Vector2 a, Vector2 b, Vector2 c, Color color) @trusted {
603 
604         openglDraw();
605         glBegin(GL_TRIANGLES);
606         glColor4ub(color.tupleof);
607         vertex(toSdpyCoords(a));
608         vertex(toSdpyCoords(b));
609         vertex(toSdpyCoords(c));
610         glEnd();
611 
612     }
613 
614     void drawRectangle(Rectangle rectangle, Color color) @trusted {
615 
616         drawRectangleImpl(toSdpyCoords(rectangle), color);
617 
618     }
619 
620     private void drawRectangleImpl(Rectangle rectangle, Color color) @trusted {
621 
622         import fluid.utils;
623 
624         openglDraw();
625         glBegin(GL_TRIANGLES);
626         glColor4ub(color.tupleof);
627 
628         //  d--c
629         //  | /|
630         //  |/ |
631         //  a--b
632         const a = start(rectangle) + Vector2(0, rectangle.height);
633         const b = end(rectangle);
634         const d = start(rectangle);
635         const c = start(rectangle) + Vector2(rectangle.width, 0);
636 
637         // First triangle
638         glTexCoord2f(0, 0);
639         vertex(d);
640         glTexCoord2f(0, 1);
641         vertex(a);
642         glTexCoord2f(1, 0);
643         vertex(c);
644 
645         // Second triangle
646         glTexCoord2f(1, 0);
647         vertex(c);
648         glTexCoord2f(0, 1);
649         vertex(a);
650         glTexCoord2f(1, 1);
651         vertex(b);
652 
653         glEnd();
654 
655     }
656 
657     void drawTexture(Texture texture, Rectangle rectangle, Color tint, string altText) @trusted
658     in (false)
659     do {
660 
661         // TODO filtering?
662         drawTextureImpl(texture, rectangle, tint, altText, true);
663 
664     }
665 
666     void drawTextureAlign(Texture texture, Rectangle rectangle, Color tint, string altText) @trusted
667     in (false)
668     do {
669 
670         drawTextureImpl(texture, rectangle, tint, altText, true);
671 
672     }
673 
674     @trusted
675     private void drawTextureImpl(Texture texture, Rectangle rectangle, Color tint, string altText, bool alignPixels) {
676 
677         import std.math;
678 
679         rectangle = toSdpyCoords(rectangle);
680 
681         if (alignPixels) {
682             rectangle.x = floor(rectangle.x);
683             rectangle.y = floor(rectangle.y);
684         }
685 
686         glEnable(GL_TEXTURE_2D);
687         glBindTexture(GL_TEXTURE_2D, texture.id);
688         drawRectangleImpl(rectangle, tint);
689         glBindTexture(GL_TEXTURE_2D, 0);
690 
691     }
692 
693 }
694 
695 int scrollValue(arsd.simpledisplay.MouseButton button) {
696 
697     switch (button) {
698 
699         case button.wheelUp: return -1;
700         case button.wheelDown: return +1;
701         default: return 0;
702 
703     }
704 
705 }
706 
707 MouseButton toFluid(arsd.simpledisplay.MouseButton button) {
708 
709      switch (button) {
710 
711         default:
712         case button.wheelUp:
713         case button.wheelDown:
714         case button.none: return MouseButton.none;
715         case button.left: return MouseButton.left;
716         case button.middle: return MouseButton.middle;
717         case button.right: return MouseButton.right;
718         case button.backButton: return MouseButton.back;
719         case button.forwardButton: return MouseButton.forward;
720 
721     }
722 
723 }
724 
725 KeyboardKey toFluid(arsd.simpledisplay.Key key) {
726 
727      switch (key) {
728 
729         default: return KeyboardKey.none;
730         case key.Escape: return KeyboardKey.escape;
731         case key.Backspace: return KeyboardKey.backspace;
732         case key.F1: return KeyboardKey.f1;
733         case key.F2: return KeyboardKey.f2;
734         case key.F3: return KeyboardKey.f3;
735         case key.F4: return KeyboardKey.f4;
736         case key.F5: return KeyboardKey.f5;
737         case key.F6: return KeyboardKey.f6;
738         case key.F7: return KeyboardKey.f7;
739         case key.F8: return KeyboardKey.f8;
740         case key.F9: return KeyboardKey.f9;
741         case key.F10: return KeyboardKey.f10;
742         case key.F11: return KeyboardKey.f11;
743         case key.F12: return KeyboardKey.f12;
744         case key.PrintScreen: return KeyboardKey.printScreen;
745         case key.ScrollLock: return KeyboardKey.scrollLock;
746         case key.Pause: return KeyboardKey.pause;
747         case key.Grave: return KeyboardKey.grave;
748         case key.N0: return KeyboardKey.digit0;
749         case key.N1: return KeyboardKey.digit1;
750         case key.N2: return KeyboardKey.digit2;
751         case key.N3: return KeyboardKey.digit3;
752         case key.N4: return KeyboardKey.digit4;
753         case key.N5: return KeyboardKey.digit5;
754         case key.N6: return KeyboardKey.digit6;
755         case key.N7: return KeyboardKey.digit7;
756         case key.N8: return KeyboardKey.digit8;
757         case key.N9: return KeyboardKey.digit9;
758         case key.Dash: return KeyboardKey.dash;
759         case key.Equals: return KeyboardKey.equal;
760         case key.Backslash: return KeyboardKey.backslash;
761         case key.Insert: return KeyboardKey.insert;
762         case key.Home: return KeyboardKey.home;
763         case key.PageUp: return KeyboardKey.pageUp;
764         case key.PageDown: return KeyboardKey.pageDown;
765         case key.Delete: return KeyboardKey.del;
766         case key.End: return KeyboardKey.end;
767         case key.Up: return KeyboardKey.up;
768         case key.Down: return KeyboardKey.down;
769         case key.Left: return KeyboardKey.left;
770         case key.Right: return KeyboardKey.right;
771         case key.Tab: return KeyboardKey.tab;
772         case key.Q: return KeyboardKey.q;
773         case key.W: return KeyboardKey.w;
774         case key.E: return KeyboardKey.e;
775         case key.R: return KeyboardKey.r;
776         case key.T: return KeyboardKey.t;
777         case key.Y: return KeyboardKey.y;
778         case key.U: return KeyboardKey.u;
779         case key.I: return KeyboardKey.i;
780         case key.O: return KeyboardKey.o;
781         case key.P: return KeyboardKey.p;
782         case key.LeftBracket: return KeyboardKey.leftBracket;
783         case key.RightBracket: return KeyboardKey.rightBracket;
784         case key.CapsLock: return KeyboardKey.capsLock;
785         case key.A: return KeyboardKey.a;
786         case key.S: return KeyboardKey.s;
787         case key.D: return KeyboardKey.d;
788         case key.F: return KeyboardKey.f;
789         case key.G: return KeyboardKey.g;
790         case key.H: return KeyboardKey.h;
791         case key.J: return KeyboardKey.j;
792         case key.K: return KeyboardKey.k;
793         case key.L: return KeyboardKey.l;
794         case key.Semicolon: return KeyboardKey.semicolon;
795         case key.Apostrophe: return KeyboardKey.apostrophe;
796         case key.Enter: return KeyboardKey.enter;
797         case key.Shift: return KeyboardKey.leftShift;
798         case key.Z: return KeyboardKey.z;
799         case key.X: return KeyboardKey.x;
800         case key.C: return KeyboardKey.c;
801         case key.V: return KeyboardKey.v;
802         case key.B: return KeyboardKey.b;
803         case key.N: return KeyboardKey.n;
804         case key.M: return KeyboardKey.m;
805         case key.Comma: return KeyboardKey.comma;
806         case key.Period: return KeyboardKey.period;
807         case key.Slash: return KeyboardKey.slash;
808         case key.Shift_r: return KeyboardKey.rightShift;
809         case key.Ctrl: return KeyboardKey.leftControl;
810         case key.Windows: return KeyboardKey.leftSuper;
811         case key.Alt: return KeyboardKey.leftAlt;
812         case key.Space: return KeyboardKey.space;
813         case key.Alt_r: return KeyboardKey.rightAlt;
814         case key.Windows_r: return KeyboardKey.rightSuper;
815         case key.Menu: return KeyboardKey.contextMenu;
816         case key.Ctrl_r: return KeyboardKey.rightControl;
817         case key.NumLock: return KeyboardKey.numLock;
818         case key.Divide: return KeyboardKey.keypadDivide;
819         case key.Multiply: return KeyboardKey.keypadMultiply;
820         case key.Minus: return KeyboardKey.keypadSubtract;
821         case key.Plus: return KeyboardKey.keypadSum;
822         case key.PadEnter: return KeyboardKey.keypadEnter;
823         case key.Pad0: return KeyboardKey.keypad0;
824         case key.Pad1: return KeyboardKey.keypad1;
825         case key.Pad2: return KeyboardKey.keypad2;
826         case key.Pad3: return KeyboardKey.keypad3;
827         case key.Pad4: return KeyboardKey.keypad4;
828         case key.Pad5: return KeyboardKey.keypad5;
829         case key.Pad6: return KeyboardKey.keypad6;
830         case key.Pad7: return KeyboardKey.keypad7;
831         case key.Pad8: return KeyboardKey.keypad8;
832         case key.Pad9: return KeyboardKey.keypad9;
833         case key.PadDot: return KeyboardKey.keypadDecimal;
834 
835     }
836 
837 }
838 
839 MouseCursor toSimpleDisplay(FluidMouseCursor cursor) @trusted {
840 
841     switch (cursor.system) {
842 
843         default:
844         case cursor.system.systemDefault:
845         case cursor.system.none:
846             return GenericCursor.Default;
847 
848         case cursor.system.pointer: return GenericCursor.Hand;
849         case cursor.system.crosshair: return GenericCursor.Cross;
850         case cursor.system.text: return GenericCursor.Text;
851         case cursor.system.allScroll: return GenericCursor.Move;
852         case cursor.system.resizeEW: return GenericCursor.SizeWe;
853         case cursor.system.resizeNS: return GenericCursor.SizeNs;
854         case cursor.system.resizeNESW: return GenericCursor.SizeNesw;
855         case cursor.system.resizeNWSE: return GenericCursor.SizeNwse;
856         case cursor.system.notAllowed: return GenericCursor.NotAllowed;
857 
858     }
859 
860 }