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