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 /*
24 * This code is based on Broken Sword 2.5 engine
25 *
26 * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
27 *
28 * Licensed under GNU GPL v2
29 *
30 */
31
32 #include "common/algorithm.h"
33 #include "common/events.h"
34 #include "common/system.h"
35 #include "common/util.h"
36
37 #include "sword25/sword25.h"
38 #include "sword25/kernel/kernel.h"
39 #include "sword25/kernel/inputpersistenceblock.h"
40 #include "sword25/kernel/outputpersistenceblock.h"
41 #include "sword25/input/inputengine.h"
42
43 namespace Sword25 {
44
45 #define DOUBLE_CLICK_TIME 500
46 #define DOUBLE_CLICK_RECT_SIZE 4
47
InputEngine(Kernel * pKernel)48 InputEngine::InputEngine(Kernel *pKernel) :
49 Service(pKernel),
50 _currentState(0),
51 _leftMouseDown(false),
52 _rightMouseDown(false),
53 _mouseX(0),
54 _mouseY(0),
55 _leftDoubleClick(false),
56 _doubleClickTime(DOUBLE_CLICK_TIME),
57 _doubleClickRectWidth(DOUBLE_CLICK_RECT_SIZE),
58 _doubleClickRectHeight(DOUBLE_CLICK_RECT_SIZE),
59 _lastLeftClickTime(0),
60 _lastLeftClickMouseX(0),
61 _lastLeftClickMouseY(0) {
62 memset(_keyboardState[0], 0, sizeof(_keyboardState[0]));
63 memset(_keyboardState[1], 0, sizeof(_keyboardState[1]));
64 _leftMouseState[0] = false;
65 _leftMouseState[1] = false;
66 _rightMouseState[0] = false;
67 _rightMouseState[1] = false;
68
69 if (!registerScriptBindings())
70 error("Script bindings could not be registered.");
71 else
72 debugC(kDebugScript, "Script bindings registered.");
73 }
74
~InputEngine()75 InputEngine::~InputEngine() {
76 unregisterScriptBindings();
77 }
78
init()79 bool InputEngine::init() {
80 // No initialisation needed
81 return true;
82 }
83
update()84 void InputEngine::update() {
85 Common::Event event;
86
87 // We keep two sets of keyboard states: The current one, and that of
88 // the previous frame. This allows us to detect which keys changed
89 // state. Also, by keeping a single central keystate array, we
90 // ensure that all script queries for key state during a single
91 // frame get the same consistent replies.
92 _currentState ^= 1;
93 memcpy(_keyboardState[_currentState], _keyboardState[_currentState ^ 1], sizeof(_keyboardState[0]));
94
95 // Loop through processing any pending events
96 bool handleEvents = true;
97 while (handleEvents && g_system->getEventManager()->pollEvent(event)) {
98 switch (event.type) {
99 case Common::EVENT_LBUTTONDOWN:
100 case Common::EVENT_LBUTTONUP:
101 _leftMouseDown = event.type == Common::EVENT_LBUTTONDOWN;
102 _mouseX = event.mouse.x;
103 _mouseY = event.mouse.y;
104 handleEvents = false;
105 break;
106 case Common::EVENT_RBUTTONDOWN:
107 case Common::EVENT_RBUTTONUP:
108 _rightMouseDown = event.type == Common::EVENT_RBUTTONDOWN;
109 _mouseX = event.mouse.x;
110 _mouseY = event.mouse.y;
111 handleEvents = false;
112 break;
113
114 case Common::EVENT_MOUSEMOVE:
115 _mouseX = event.mouse.x;
116 _mouseY = event.mouse.y;
117 break;
118
119 case Common::EVENT_KEYDOWN:
120 case Common::EVENT_KEYUP:
121 alterKeyboardState(event.kbd.keycode, (event.type == Common::EVENT_KEYDOWN) ? 0x80 : 0);
122 break;
123
124 default:
125 break;
126 }
127 }
128
129 _leftMouseState[_currentState] = _leftMouseDown;
130 _rightMouseState[_currentState] = _rightMouseDown;
131
132 testForLeftDoubleClick();
133 }
134
isLeftMouseDown()135 bool InputEngine::isLeftMouseDown() {
136 return _leftMouseDown;
137 }
138
isRightMouseDown()139 bool InputEngine::isRightMouseDown() {
140 return _rightMouseDown;
141 }
142
testForLeftDoubleClick()143 void InputEngine::testForLeftDoubleClick() {
144 _leftDoubleClick = false;
145
146 // Only bother checking for a double click if the left mouse button was clicked
147 if (wasLeftMouseDown()) {
148 // Get the time now
149 uint now = Kernel::getInstance()->getMilliTicks();
150
151 // A double click is signalled if
152 // 1. The two clicks are close enough together
153 // 2. The mouse cursor hasn't moved much
154 if (now - _lastLeftClickTime <= _doubleClickTime &&
155 ABS(_mouseX - _lastLeftClickMouseX) <= _doubleClickRectWidth / 2 &&
156 ABS(_mouseY - _lastLeftClickMouseY) <= _doubleClickRectHeight / 2) {
157 _leftDoubleClick = true;
158
159 // Reset the time and position of the last click, so that clicking is not
160 // interpreted as the first click of a further double-click
161 _lastLeftClickTime = 0;
162 _lastLeftClickMouseX = 0;
163 _lastLeftClickMouseY = 0;
164 } else {
165 // There is no double click. Remember the position and time of the click,
166 // in case it's the first click of a double-click sequence
167 _lastLeftClickTime = now;
168 _lastLeftClickMouseX = _mouseX;
169 _lastLeftClickMouseY = _mouseY;
170 }
171 }
172 }
173
alterKeyboardState(int keycode,byte newState)174 void InputEngine::alterKeyboardState(int keycode, byte newState) {
175 assert(keycode < ARRAYSIZE(_keyboardState[_currentState]));
176 _keyboardState[_currentState][keycode] = newState;
177 }
178
isLeftDoubleClick()179 bool InputEngine::isLeftDoubleClick() {
180 return _leftDoubleClick;
181 }
182
wasLeftMouseDown()183 bool InputEngine::wasLeftMouseDown() {
184 return (_leftMouseState[_currentState] == false) && (_leftMouseState[_currentState ^ 1] == true);
185 }
186
wasRightMouseDown()187 bool InputEngine::wasRightMouseDown() {
188 return (_rightMouseState[_currentState] == false) && (_rightMouseState[_currentState ^ 1] == true);
189 }
190
getMouseX()191 int InputEngine::getMouseX() {
192 return _mouseX;
193 }
194
getMouseY()195 int InputEngine::getMouseY() {
196 return _mouseY;
197 }
198
isKeyDown(uint keyCode)199 bool InputEngine::isKeyDown(uint keyCode) {
200 assert(keyCode < ARRAYSIZE(_keyboardState[_currentState]));
201 return (_keyboardState[_currentState][keyCode] & 0x80) != 0;
202 }
203
wasKeyDown(uint keyCode)204 bool InputEngine::wasKeyDown(uint keyCode) {
205 assert(keyCode < ARRAYSIZE(_keyboardState[_currentState]));
206 return ((_keyboardState[_currentState][keyCode] & 0x80) == 0) &&
207 ((_keyboardState[_currentState ^ 1][keyCode] & 0x80) != 0);
208 }
209
setCharacterCallback(CharacterCallback callback)210 void InputEngine::setCharacterCallback(CharacterCallback callback) {
211 _characterCallback = callback;
212 }
213
setCommandCallback(CommandCallback callback)214 void InputEngine::setCommandCallback(CommandCallback callback) {
215 _commandCallback = callback;
216 }
217
reportCharacter(byte character)218 void InputEngine::reportCharacter(byte character) {
219 if (_characterCallback)
220 (*_characterCallback)(character);
221 }
222
reportCommand(KEY_COMMANDS command)223 void InputEngine::reportCommand(KEY_COMMANDS command) {
224 if (_commandCallback)
225 (*_commandCallback)(command);
226 }
227
persist(OutputPersistenceBlock & writer)228 bool InputEngine::persist(OutputPersistenceBlock &writer) {
229 // Write out the number of command callbacks and their names.
230 // Note: We do this only for compatibility with older engines resp.
231 // the original engine.
232 writer.write((uint32)1);
233 writer.writeString("LuaCommandCB");
234
235 // Write out the number of command callbacks and their names.
236 // Note: We do this only for compatibility with older engines resp.
237 // the original engine.
238 writer.write((uint32)1);
239 writer.writeString("LuaCharacterCB");
240
241 return true;
242 }
243
unpersist(InputPersistenceBlock & reader)244 bool InputEngine::unpersist(InputPersistenceBlock &reader) {
245 Common::String callbackFunctionName;
246
247 // Read number of command callbacks and their names.
248 // Note: We do this only for compatibility with older engines resp.
249 // the original engine.
250 uint32 commandCallbackCount;
251 reader.read(commandCallbackCount);
252 assert(commandCallbackCount == 1);
253
254 reader.readString(callbackFunctionName);
255 assert(callbackFunctionName == "LuaCommandCB");
256
257 // Read number of character callbacks and their names.
258 // Note: We do this only for compatibility with older engines resp.
259 // the original engine.
260 uint32 characterCallbackCount;
261 reader.read(characterCallbackCount);
262 assert(characterCallbackCount == 1);
263
264 reader.readString(callbackFunctionName);
265 assert(callbackFunctionName == "LuaCharacterCB");
266
267 return reader.isGood();
268 }
269
270 } // End of namespace Sword25
271