1 /**
2  * Mouse
3  */
4 module d2d.sdl2.Mouse;
5 
6 import std.algorithm;
7 import std.datetime;
8 import d2d.sdl2;
9 
10 ///A list of all of the button codes
11 immutable allButtonCodes = [
12     SDL_BUTTON_LEFT, SDL_BUTTON_MIDDLE, SDL_BUTTON_RIGHT, SDL_BUTTON_X1, SDL_BUTTON_X2
13 ];
14 
15 /**
16  * The mouse input source which acculmulates mouse information
17  */
18 class Mouse : InputSource!uint, EventHandler {
19 
20     private Pressable!uint[uint] _allButtons; ///All of the buttons on the mouse
21     private iVector _totalWheelDisplacement; ///Total displacement of the mousewheel since mouse construction
22     private iVector _location; ///The location of the mouse accounting for logical size or viewport
23 
24     alias allButtons = allPressables; ///Allows allPressables to be called as allButtons
25 
26     /**
27      * Returns all of the mouse buttons
28      */
29     override @property Pressable!uint[uint] allPressables() {
30         return this._allButtons.dup;
31     }
32 
33     /**
34      * Gets the mouse location accounting for logical size or viewport
35      * This should be what is most regularly used
36      */
37     @property iVector location() {
38         return new iVector(this._location.components);
39     }
40 
41     /**
42      * Sets the location of the mouse relative to the window
43      */
44     @property void windowLocation(iVector location) {
45         SDL_WarpMouseInWindow(null, location.x, location.y);
46     }
47 
48     /**
49      * Gets the location of the mouse
50      */
51     @property iVector windowLocation() {
52         iVector location = new iVector(-1, -1);
53         SDL_GetMouseState(&location.x, &location.y);
54         return location;
55     }
56 
57     /**
58      * Sets the location of the mouse globally
59      */
60     @property void screenLocation(iVector location) {
61         SDL_WarpMouseGlobal(location.x, location.y);
62     }
63 
64     /**
65      * Gets the location of the mouse globally
66      */
67     @property iVector screenLocation() {
68         iVector location = new iVector(-1, -1);
69         SDL_GetGlobalMouseState(&location.x, &location.y);
70         return location;
71     }
72 
73     /**
74      * Gets by how much the mouse wheel has been displaced
75      * Records changes in wheel from the start of mouse construction
76      */
77     @property iVector totalWheelDisplacement() {
78         return new iVector(this._totalWheelDisplacement.components);
79     }
80 
81     /**
82      * Sets the cursor of the mouse
83      */
84     @property void cursor(Cursor newCursor) {
85         SDL_SetCursor(newCursor.handle);
86     }
87 
88     /**
89      * Gets the cursor of the mouse
90      * Special precautions must be taken when using this method:
91      * Make sure to store the output of the cursor or make sure the cursor doesn't get GCed
92      * Because the actual cursor is being used in C, D will think this returned cursor won't be being used and destroy it
93      * It is probably better to avoid this method entirely
94      */
95     @property Cursor cursor() {
96         return new Cursor(ensureSafe(SDL_GetCursor()));
97     }
98 
99     /**
100      * Makes a mouse and initializes all of the buttons
101      */
102     this() {
103         allButtonCodes.each!(
104                 buttonCode => this._allButtons[buttonCode] = new Pressable!uint(buttonCode));
105         this._totalWheelDisplacement = new iVector(0, 0);
106         this._location = new iVector(0, 0);
107     }
108 
109     /**
110      * Acculmulates all of the mouse events and updates stored pressables accordingly
111      */
112     override void handleEvent(SDL_Event event) {
113         switch (event.type) {
114         case SDL_MOUSEMOTION:
115             this._location.x = event.button.x;
116             this._location.y = event.button.y;
117             break;
118         case SDL_MOUSEBUTTONDOWN:
119             this._allButtons[event.button.button].lastPressed = Clock.currTime();
120             break;
121         case SDL_MOUSEBUTTONUP:
122             this._allButtons[event.button.button].lastReleased = Clock.currTime();
123             break;
124         case SDL_MOUSEWHEEL:
125             this._totalWheelDisplacement.x += event.wheel.x;
126             this._totalWheelDisplacement.y += event.wheel.y;
127             break;
128         default:
129             break;
130         }
131     }
132 
133 }
134 
135 /**
136  * A cursor is how the mouse at its location looks
137  * While this class should *technically* be defined in d2d.sdl2, its only use is in Mouse
138  * And since this class is small, instead of giving it its own file, I'll keep it here
139  */
140 class Cursor {
141 
142     private SDL_Cursor* cursor;
143 
144     /**
145      * Returns the raw SDL data of this object
146      */
147     @property handle() {
148         return this.cursor;
149     }
150 
151     /**
152      * Creates a cursor from a surface and the hotspot
153      * The hotspot is where on the surface is the actual mouse location
154      */
155     this(Surface appearance, int hotspotX, int hotspotY) {
156         this.cursor = ensureSafe(SDL_CreateColorCursor(appearance.handle, hotspotX, hotspotY));
157     }
158 
159     /**
160      * Creates a cursor from a predefined system cursor
161      */
162     this(SDL_SystemCursor id) {
163         this.cursor = ensureSafe(SDL_CreateSystemCursor(id));
164     }
165 
166     /**
167      * Creates a cursor from an already SDL_Cursor
168      */
169     this(SDL_Cursor* alreadyExisting) {
170         this.cursor = alreadyExisting;
171     }
172 
173     /**
174      * Ensures that SDL can properly dispose of the cursor
175      */
176     ~this() {
177         SDL_FreeCursor(this.cursor);
178     }
179 
180 }
181 
182 /** 
183  * Gets the system's default cursor
184  */
185 Cursor defaultCursor() {
186     return new Cursor(ensureSafe(SDL_GetDefaultCursor()));
187 }