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
28namespace love
29{
30namespace mouse
31{
32namespace 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.)
37static 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.
45static 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
52const char *Mouse::getName() const
53{
54 return "love.mouse.sdl";
55}
56
57Mouse::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
65Mouse::~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
76love::mouse::Cursor *Mouse::newCursor(love::image::ImageData *data, int hotx, int hoty)
77{
78 return new Cursor(data, hotx, hoty);
79}
80
81love::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
97void Mouse::setCursor(love::mouse::Cursor *cursor)
98{
99 curCursor.set(cursor);
100 SDL_SetCursor((SDL_Cursor *) cursor->getHandle());
101}
102
103void Mouse::setCursor()
104{
105 curCursor.set(nullptr);
106 SDL_SetCursor(SDL_GetDefaultCursor());
107}
108
109love::mouse::Cursor *Mouse::getCursor() const
110{
111 return curCursor.get();
112}
113
114
115bool Mouse::isCursorSupported() const
116{
117 return SDL_GetDefaultCursor() != nullptr;
118}
119
120double 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
131double 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
142void 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
152void 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
169void Mouse::setX(double x)
170{
171 setPosition(x, getY());
172}
173
174void Mouse::setY(double y)
175{
176 setPosition(getX(), y);
177}
178
179void Mouse::setVisible(bool visible)
180{
181 SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE);
182}
183
184bool 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
212bool Mouse::isVisible() const
213{
214 return SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE;
215}
216
217void Mouse::setGrabbed(bool grab)
218{
219 auto window = Module::getInstance<window::Window>(Module::M_WINDOW);
220 if (window)
221 window->setMouseGrab(grab);
222}
223
224bool 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
233bool Mouse::setRelativeMode(bool relative)
234{
235 return SDL_SetRelativeMouseMode(relative ? SDL_TRUE : SDL_FALSE) == 0;
236}
237
238bool Mouse::getRelativeMode() const
239{
240 return SDL_GetRelativeMouseMode() != SDL_FALSE;
241}
242
243} // sdl
244} // mouse
245} // love
246