1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "common/events.h"
24 #include "ags/engine/ac/sys_events.h"
25 //include <deque>
26 #include "ags/shared/core/platform.h"
27 #include "ags/shared/ac/common.h"
28 #include "ags/shared/ac/game_setup_struct.h"
29 #include "ags/shared/ac/keycode.h"
30 #include "ags/engine/ac/mouse.h"
31 #include "ags/engine/ac/timer.h"
32 #include "ags/engine/device/mouse_w32.h"
33 #include "ags/engine/platform/base/ags_platform_driver.h"
34 #include "ags/engine/main/engine.h"
35 #include "ags/ags.h"
36 #include "ags/events.h"
37 #include "ags/globals.h"
38 
39 // TODO: Replace me
40 typedef int SDL_Scancode;
41 
42 
43 namespace AGS3 {
44 
45 // TODO: check later, if this may be useful in other places (then move to header)
46 enum eAGSMouseButtonMask {
47 	MouseBitLeft = 0x01,
48 	MouseBitRight = 0x02,
49 	MouseBitMiddle = 0x04,
50 	MouseBitX1 = 0x08,
51 	MouseBitX2 = 0x10
52 };
53 
54 using namespace AGS::Shared;
55 using namespace AGS::Engine;
56 
57 extern void domouse(int str);
58 const int MB_ARRAY[3] = { MouseBitLeft, MouseBitRight, MouseBitMiddle };
59 static void(*_on_quit_callback)(void) = nullptr;
60 static void(*_on_switchin_callback)(void) = nullptr;
61 static void(*_on_switchout_callback)(void) = nullptr;
62 
63 // ----------------------------------------------------------------------------
64 // KEYBOARD INPUT
65 // ----------------------------------------------------------------------------
66 
ags_keycode_from_scummvm(const Common::Event & event)67 KeyInput ags_keycode_from_scummvm(const Common::Event &event) {
68 	KeyInput ki;
69 
70 	ki.Key = ::AGS::g_events->scummvm_key_to_ags_key(event);
71 
72 	return ki;
73 }
74 
ags_keyevent_ready()75 bool ags_keyevent_ready() {
76 	return ::AGS::g_events->keyEventPending();
77 }
78 
ags_get_next_keyevent()79 Common::Event ags_get_next_keyevent() {
80 	return ::AGS::g_events->getPendingKeyEvent();
81 }
82 
ags_iskeydown(eAGSKeyCode ags_key)83 int ags_iskeydown(eAGSKeyCode ags_key) {
84 	return ::AGS::g_events->isKeyPressed(ags_key);
85 }
86 
ags_simulate_keypress(eAGSKeyCode ags_key)87 void ags_simulate_keypress(eAGSKeyCode ags_key) {
88 	Common::KeyCode keycode[3];
89 	if (!::AGS::EventsManager::ags_key_to_scancode(ags_key, keycode))
90 		return;
91 
92 	// Push a key event to the event queue; note that this won't affect the key states array
93 	Common::Event e;
94 	e.type = Common::EVENT_KEYDOWN;
95 	e.kbd.keycode = keycode[0];
96 	e.kbd.ascii = (e.kbd.keycode >= 32 && e.kbd.keycode <= 127) ? e.kbd.keycode : 0;
97 
98 	::AGS::g_events->pushKeyboardEvent(e);
99 }
100 
101 // ----------------------------------------------------------------------------
102 // MOUSE INPUT
103 // ----------------------------------------------------------------------------
104 
scummvm_button_to_mask(Common::EventType type)105 static int scummvm_button_to_mask(Common::EventType type) {
106 	switch (type) {
107 	case Common::EVENT_LBUTTONDOWN:
108 	case Common::EVENT_LBUTTONUP:
109 		return MouseBitLeft;
110 	case Common::EVENT_RBUTTONDOWN:
111 	case Common::EVENT_RBUTTONUP:
112 		return MouseBitRight;
113 	case Common::EVENT_MBUTTONDOWN:
114 	case Common::EVENT_MBUTTONUP:
115 		return MouseBitMiddle;
116 	default:
117 		return 0;
118 	}
119 }
120 
121 // Returns accumulated mouse button state and clears internal cache by timer
mouse_button_poll()122 static int mouse_button_poll() {
123 	auto now = AGS_Clock::now();
124 	int result = _G(mouse_button_state) | _G(mouse_accum_button_state);
125 	if (now >= _G(mouse_clear_at_time)) {
126 		_G(mouse_accum_button_state) = 0;
127 		_G(mouse_clear_at_time) = now + std::chrono::milliseconds(50);
128 	}
129 	return result;
130 }
131 
on_mouse_motion(const Common::Event & event)132 static void on_mouse_motion(const Common::Event &event) {
133 	_G(sys_mouse_x) = event.mouse.x;
134 	_G(sys_mouse_y) = event.mouse.y;
135 	_G(mouse_accum_relx) += event.relMouse.x;
136 	_G(mouse_accum_rely) += event.relMouse.y;
137 }
138 
on_mouse_button(const Common::Event & event)139 static void on_mouse_button(const Common::Event &event) {
140 	_G(sys_mouse_x) = event.mouse.x;
141 	_G(sys_mouse_y) = event.mouse.y;
142 
143 	if (event.type == Common::EVENT_LBUTTONDOWN ||
144 			event.type == Common::EVENT_RBUTTONDOWN ||
145 			event.type == Common::EVENT_MBUTTONDOWN) {
146 		_G(mouse_button_state) |= scummvm_button_to_mask(event.type);
147 		_G(mouse_accum_button_state) |= scummvm_button_to_mask(event.type);
148 	} else {
149 		_G(mouse_button_state) &= ~scummvm_button_to_mask(event.type);
150 	}
151 }
152 
on_mouse_wheel(const Common::Event & event)153 static void on_mouse_wheel(const Common::Event &event) {
154 	if (event.type == Common::EVENT_WHEELDOWN)
155 		_G(sys_mouse_z)++;
156 	else
157 		_G(sys_mouse_z)--;
158 }
159 
mgetbutton()160 int mgetbutton() {
161 	int toret = MouseNone;
162 	int butis = mouse_button_poll();
163 
164 	if ((butis > 0) & (_G(butwas) > 0))
165 		return MouseNone;  // don't allow holding button down
166 
167 	if (butis & MouseBitLeft)
168 		toret = MouseLeft;
169 	else if (butis & MouseBitRight)
170 		toret = MouseRight;
171 	else if (butis & MouseBitMiddle)
172 		toret = MouseMiddle;
173 
174 	_G(butwas) = butis;
175 	return toret;
176 
177 	// TODO: presumably this was a hack for 1-button Mac mouse;
178 	// is this still necessary?
179 	// find an elegant way to reimplement this; e.g. allow to configure key->mouse mappings?!
180 #define AGS_SIMULATE_RIGHT_CLICK (AGS_PLATFORM_OS_MACOS)
181 #if defined (AGS_SIMULATE_RIGHT_CLICK__FIXME)
182 	// j Ctrl-left click should be right-click
183 	if (ags_iskeypressed(__allegro_KEY_LCONTROL) || ags_iskeypressed(__allegro_KEY_RCONTROL)) {
184 		toret = RIGHT;
185 	}
186 #endif
187 	return 0;
188 }
189 
ags_misbuttondown(int but)190 bool ags_misbuttondown(int but) {
191 	return (mouse_button_poll() & MB_ARRAY[but]) != 0;
192 }
193 
ags_mgetbutton()194 int ags_mgetbutton() {
195 	int result;
196 
197 	if (_G(pluginSimulatedClick) > MouseNone) {
198 		result = _G(pluginSimulatedClick);
199 		_G(pluginSimulatedClick) = MouseNone;
200 	} else {
201 		result = mgetbutton();
202 	}
203 	return result;
204 }
205 
ags_mouse_get_relxy(int & x,int & y)206 void ags_mouse_get_relxy(int &x, int &y) {
207 	x = _G(mouse_accum_relx);
208 	y = _G(mouse_accum_rely);
209 	_G(mouse_accum_relx) = 0;
210 	_G(mouse_accum_rely) = 0;
211 }
212 
ags_domouse(int what)213 void ags_domouse(int what) {
214 	// do mouse is "update the mouse x,y and also the cursor position", unless DOMOUSE_NOCURSOR is set.
215 	if (what == DOMOUSE_NOCURSOR)
216 		mgetgraphpos();
217 	else
218 		domouse(what);
219 }
220 
ags_check_mouse_wheel()221 int ags_check_mouse_wheel() {
222 	if (_GP(game).options[OPT_MOUSEWHEEL] == 0) {
223 		return 0;
224 	}
225 	if (_G(sys_mouse_z) == _G(mouse_z_was)) {
226 		return 0;
227 	}
228 
229 	int result = 0;
230 	if (_G(sys_mouse_z) > _G(mouse_z_was))
231 		result = 1;   // eMouseWheelNorth
232 	else
233 		result = -1;  // eMouseWheelSouth
234 	_G(mouse_z_was) = _G(sys_mouse_z);
235 	return result;
236 }
237 
ags_clear_input_state()238 void ags_clear_input_state() {
239 	// Clear everything related to the input field
240 	::AGS::g_events->clearEvents();
241 	_G(mouse_accum_relx) = 0;
242 	_G(mouse_accum_rely) = 0;
243 	_G(mouse_button_state) = 0;
244 	_G(mouse_accum_button_state) = 0;
245 	_G(mouse_clear_at_time) = AGS_Clock::now();
246 }
247 
ags_clear_input_buffer()248 void ags_clear_input_buffer() {
249 	::AGS::g_events->clearEvents();
250 	// accumulated state only helps to not miss clicks
251 	_G(mouse_accum_button_state) = 0;
252 	// forget about recent mouse relative movement too
253 	_G(mouse_accum_relx) = 0;
254 	_G(mouse_accum_rely) = 0;
255 }
256 
ags_clear_mouse_movement()257 void ags_clear_mouse_movement() {
258 	_G(mouse_accum_relx) = 0;
259 	_G(mouse_accum_rely) = 0;
260 }
261 
262 // TODO: this is an awful function that should be removed eventually.
263 // Must replace with proper updateable game state.
ags_wait_until_keypress()264 void ags_wait_until_keypress() {
265 	do {
266 		sys_evt_process_pending();
267 		_G(platform)->YieldCPU();
268 	} while (!SHOULD_QUIT && !ags_keyevent_ready());
269 	ags_clear_input_buffer();
270 }
271 
272 
273 // ----------------------------------------------------------------------------
274 // EVENTS
275 // ----------------------------------------------------------------------------
276 
sys_evt_set_quit_callback(void (* proc)(void))277 void sys_evt_set_quit_callback(void(*proc)(void)) {
278 	_on_quit_callback = proc;
279 }
280 
sys_evt_set_focus_callbacks(void (* switch_in)(void),void (* switch_out)(void))281 void sys_evt_set_focus_callbacks(void(*switch_in)(void), void(*switch_out)(void)) {
282 	_on_switchin_callback = switch_in;
283 	_on_switchout_callback = switch_out;
284 }
285 
sys_process_event(const Common::Event & event)286 static void sys_process_event(const Common::Event &event) {
287 	switch (event.type) {
288 	case Common::EVENT_MOUSEMOVE:
289 		on_mouse_motion(event);
290 		break;
291 	case Common::EVENT_LBUTTONDOWN:
292 	case Common::EVENT_RBUTTONDOWN:
293 	case Common::EVENT_MBUTTONDOWN:
294 	case Common::EVENT_LBUTTONUP:
295 	case Common::EVENT_RBUTTONUP:
296 	case Common::EVENT_MBUTTONUP:
297 		on_mouse_button(event);
298 		break;
299 	case Common::EVENT_WHEELDOWN:
300 	case Common::EVENT_WHEELUP:
301 		on_mouse_wheel(event);
302 		break;
303 	default:
304 		break;
305 	}
306 }
307 
sys_evt_process_pending(void)308 void sys_evt_process_pending(void) {
309 	::AGS::g_events->pollEvents();
310 	Common::Event e;
311 
312 	while ((e = ::AGS::g_events->readEvent()).type != Common::EVENT_INVALID)
313 		sys_process_event(e);
314 }
315 
316 } // namespace AGS3
317