1 /**
2  * Copyright (c) 2006-2019 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.)
windowToDPICoords(double * x,double * y)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.
DPIToWindowCoords(double * x,double * y)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 
getName() const52 const char *Mouse::getName() const
53 {
54 	return "love.mouse.sdl";
55 }
56 
Mouse()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 
~Mouse()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 
newCursor(love::image::ImageData * data,int hotx,int hoty)76 love::mouse::Cursor *Mouse::newCursor(love::image::ImageData *data, int hotx, int hoty)
77 {
78 	return new Cursor(data, hotx, hoty);
79 }
80 
getSystemCursor(Cursor::SystemCursor cursortype)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 
setCursor(love::mouse::Cursor * cursor)97 void Mouse::setCursor(love::mouse::Cursor *cursor)
98 {
99 	curCursor.set(cursor);
100 	SDL_SetCursor((SDL_Cursor *) cursor->getHandle());
101 }
102 
setCursor()103 void Mouse::setCursor()
104 {
105 	curCursor.set(nullptr);
106 	SDL_SetCursor(SDL_GetDefaultCursor());
107 }
108 
getCursor() const109 love::mouse::Cursor *Mouse::getCursor() const
110 {
111 	return curCursor.get();
112 }
113 
114 
isCursorSupported() const115 bool Mouse::isCursorSupported() const
116 {
117 	return SDL_GetDefaultCursor() != nullptr;
118 }
119 
getX() const120 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 
getY() const131 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 
getPosition(double & x,double & y) const142 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 
setPosition(double x,double y)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 
setX(double x)169 void Mouse::setX(double x)
170 {
171 	setPosition(x, getY());
172 }
173 
setY(double y)174 void Mouse::setY(double y)
175 {
176 	setPosition(getX(), y);
177 }
178 
setVisible(bool visible)179 void Mouse::setVisible(bool visible)
180 {
181 	SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE);
182 }
183 
isDown(const std::vector<int> & buttons) const184 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 
isVisible() const212 bool Mouse::isVisible() const
213 {
214 	return SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE;
215 }
216 
setGrabbed(bool grab)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 
isGrabbed() const224 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 
setRelativeMode(bool relative)233 bool Mouse::setRelativeMode(bool relative)
234 {
235 	return SDL_SetRelativeMouseMode(relative ? SDL_TRUE : SDL_FALSE) == 0;
236 }
237 
getRelativeMode() const238 bool Mouse::getRelativeMode() const
239 {
240 	return SDL_GetRelativeMouseMode() != SDL_FALSE;
241 }
242 
243 } // sdl
244 } // mouse
245 } // love
246