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