1 | /** |
2 | * Copyright (c) 2006-2023 LOVE Development Team |
3 | * |
4 | * This software is provided 'as-is', without any express or implied |
5 | * warranty. In no event will the authors be held liable for any damages |
6 | * arising from the use of this software. |
7 | * |
8 | * Permission is granted to anyone to use this software for any purpose, |
9 | * including commercial applications, and to alter it and redistribute it |
10 | * freely, subject to the following restrictions: |
11 | * |
12 | * 1. The origin of this software must not be misrepresented; you must not |
13 | * claim that you wrote the original software. If you use this software |
14 | * in a product, an acknowledgment in the product documentation would be |
15 | * appreciated but is not required. |
16 | * 2. Altered source versions must be plainly marked as such, and must not be |
17 | * misrepresented as being the original software. |
18 | * 3. This notice may not be removed or altered from any source distribution. |
19 | **/ |
20 | |
21 | // LOVE |
22 | #include "Mouse.h" |
23 | #include "window/sdl/Window.h" |
24 | |
25 | // SDL |
26 | #include <SDL_mouse.h> |
27 | |
28 | namespace love |
29 | { |
30 | namespace mouse |
31 | { |
32 | namespace sdl |
33 | { |
34 | |
35 | // SDL reports mouse coordinates in the window coordinate system in OS X, but |
36 | // we want them in pixel coordinates (may be different with high-DPI enabled.) |
37 | static void windowToDPICoords(double *x, double *y) |
38 | { |
39 | auto window = Module::getInstance<window::Window>(Module::M_WINDOW); |
40 | if (window) |
41 | window->windowToDPICoords(x, y); |
42 | } |
43 | |
44 | // And vice versa for setting mouse coordinates. |
45 | static void DPIToWindowCoords(double *x, double *y) |
46 | { |
47 | auto window = Module::getInstance<window::Window>(Module::M_WINDOW); |
48 | if (window) |
49 | window->DPIToWindowCoords(x, y); |
50 | } |
51 | |
52 | const char *Mouse::getName() const |
53 | { |
54 | return "love.mouse.sdl" ; |
55 | } |
56 | |
57 | Mouse::Mouse() |
58 | : curCursor(nullptr) |
59 | { |
60 | // SDL may need the video subsystem in order to clean up the cursor when |
61 | // quitting. Subsystems are reference-counted. |
62 | SDL_InitSubSystem(SDL_INIT_VIDEO); |
63 | } |
64 | |
65 | Mouse::~Mouse() |
66 | { |
67 | if (curCursor.get()) |
68 | setCursor(); |
69 | |
70 | for (auto &c : systemCursors) |
71 | c.second->release(); |
72 | |
73 | SDL_QuitSubSystem(SDL_INIT_VIDEO); |
74 | } |
75 | |
76 | love::mouse::Cursor *Mouse::newCursor(love::image::ImageData *data, int hotx, int hoty) |
77 | { |
78 | return new Cursor(data, hotx, hoty); |
79 | } |
80 | |
81 | love::mouse::Cursor *Mouse::getSystemCursor(Cursor::SystemCursor cursortype) |
82 | { |
83 | Cursor *cursor = nullptr; |
84 | auto it = systemCursors.find(cursortype); |
85 | |
86 | if (it != systemCursors.end()) |
87 | cursor = it->second; |
88 | else |
89 | { |
90 | cursor = new Cursor(cursortype); |
91 | systemCursors[cursortype] = cursor; |
92 | } |
93 | |
94 | return cursor; |
95 | } |
96 | |
97 | void Mouse::setCursor(love::mouse::Cursor *cursor) |
98 | { |
99 | curCursor.set(cursor); |
100 | SDL_SetCursor((SDL_Cursor *) cursor->getHandle()); |
101 | } |
102 | |
103 | void Mouse::setCursor() |
104 | { |
105 | curCursor.set(nullptr); |
106 | SDL_SetCursor(SDL_GetDefaultCursor()); |
107 | } |
108 | |
109 | love::mouse::Cursor *Mouse::getCursor() const |
110 | { |
111 | return curCursor.get(); |
112 | } |
113 | |
114 | |
115 | bool Mouse::isCursorSupported() const |
116 | { |
117 | return SDL_GetDefaultCursor() != nullptr; |
118 | } |
119 | |
120 | double Mouse::getX() const |
121 | { |
122 | int x; |
123 | SDL_GetMouseState(&x, nullptr); |
124 | |
125 | double dx = (double) x; |
126 | windowToDPICoords(&dx, nullptr); |
127 | |
128 | return dx; |
129 | } |
130 | |
131 | double Mouse::getY() const |
132 | { |
133 | int y; |
134 | SDL_GetMouseState(nullptr, &y); |
135 | |
136 | double dy = (double) y; |
137 | windowToDPICoords(nullptr, &dy); |
138 | |
139 | return dy; |
140 | } |
141 | |
142 | void Mouse::getPosition(double &x, double &y) const |
143 | { |
144 | int mx, my; |
145 | SDL_GetMouseState(&mx, &my); |
146 | |
147 | x = (double) mx; |
148 | y = (double) my; |
149 | windowToDPICoords(&x, &y); |
150 | } |
151 | |
152 | void Mouse::setPosition(double x, double y) |
153 | { |
154 | auto window = Module::getInstance<window::Window>(Module::M_WINDOW); |
155 | |
156 | SDL_Window *handle = nullptr; |
157 | if (window) |
158 | handle = (SDL_Window *) window->getHandle(); |
159 | |
160 | DPIToWindowCoords(&x, &y); |
161 | SDL_WarpMouseInWindow(handle, (int) x, (int) y); |
162 | |
163 | // SDL_WarpMouse doesn't directly update SDL's internal mouse state in Linux |
164 | // and Windows, so we call SDL_PumpEvents now to make sure the next |
165 | // getPosition call always returns the updated state. |
166 | SDL_PumpEvents(); |
167 | } |
168 | |
169 | void Mouse::setX(double x) |
170 | { |
171 | setPosition(x, getY()); |
172 | } |
173 | |
174 | void Mouse::setY(double y) |
175 | { |
176 | setPosition(getX(), y); |
177 | } |
178 | |
179 | void Mouse::setVisible(bool visible) |
180 | { |
181 | SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE); |
182 | } |
183 | |
184 | bool Mouse::isDown(const std::vector<int> &buttons) const |
185 | { |
186 | Uint32 buttonstate = SDL_GetMouseState(nullptr, nullptr); |
187 | |
188 | for (int button : buttons) |
189 | { |
190 | if (button <= 0) |
191 | continue; |
192 | |
193 | // We use button index 2 to represent the right mouse button, but SDL |
194 | // uses 2 to represent the middle mouse button. |
195 | switch (button) |
196 | { |
197 | case 2: |
198 | button = SDL_BUTTON_RIGHT; |
199 | break; |
200 | case 3: |
201 | button = SDL_BUTTON_MIDDLE; |
202 | break; |
203 | } |
204 | |
205 | if (buttonstate & SDL_BUTTON(button)) |
206 | return true; |
207 | } |
208 | |
209 | return false; |
210 | } |
211 | |
212 | bool Mouse::isVisible() const |
213 | { |
214 | return SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE; |
215 | } |
216 | |
217 | void Mouse::setGrabbed(bool grab) |
218 | { |
219 | auto window = Module::getInstance<window::Window>(Module::M_WINDOW); |
220 | if (window) |
221 | window->setMouseGrab(grab); |
222 | } |
223 | |
224 | bool Mouse::isGrabbed() const |
225 | { |
226 | auto window = Module::getInstance<window::Window>(Module::M_WINDOW); |
227 | if (window) |
228 | return window->isMouseGrabbed(); |
229 | else |
230 | return false; |
231 | } |
232 | |
233 | bool Mouse::setRelativeMode(bool relative) |
234 | { |
235 | return SDL_SetRelativeMouseMode(relative ? SDL_TRUE : SDL_FALSE) == 0; |
236 | } |
237 | |
238 | bool Mouse::getRelativeMode() const |
239 | { |
240 | return SDL_GetRelativeMouseMode() != SDL_FALSE; |
241 | } |
242 | |
243 | } // sdl |
244 | } // mouse |
245 | } // love |
246 | |