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/system.h"
24 #include "common/events.h"
25 #include "common/file.h"
26 
27 #include "sci/sci.h"
28 #include "sci/event.h"
29 #include "sci/console.h"
30 #include "sci/engine/state.h"
31 #include "sci/engine/kernel.h"
32 #ifdef ENABLE_SCI32
33 #include "sci/graphics/cursor32.h"
34 #include "sci/graphics/frameout.h"
35 #endif
36 #include "sci/graphics/screen.h"
37 
38 namespace Sci {
39 
40 struct ScancodeRow {
41 	int offset;
42 	const char *keys;
43 };
44 
45 static const ScancodeRow scancodeAltifyRows[] = {
46 	{ 0x10, "QWERTYUIOP[]"  },
47 	{ 0x1e, "ASDFGHJKL;'\\" },
48 	{ 0x2c, "ZXCVBNM,./"    }
49 };
50 
51 static const byte codePageMap88591ToDOS[0x80] = {
52 	 '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?', // 0x8x
53 	 '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?', // 0x9x
54 	 '?', 0xad, 0x9b, 0x9c,  '?', 0x9d,  '?', 0x9e,  '?',  '?', 0xa6, 0xae, 0xaa,  '?',  '?',  '?', // 0xAx
55 	 '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?', 0xa7, 0xaf, 0xac, 0xab,  '?', 0xa8, // 0xBx
56 	 '?',  '?',  '?',  '?', 0x8e, 0x8f, 0x92, 0x80,  '?', 0x90,  '?',  '?',  '?',  '?',  '?',  '?', // 0xCx
57 	 '?', 0xa5,  '?',  '?',  '?',  '?', 0x99,  '?',  '?',  '?',  '?',  '?', 0x9a,  '?',  '?', 0xe1, // 0xDx
58 	0x85, 0xa0, 0x83,  '?', 0x84, 0x86, 0x91, 0x87, 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b, // 0xEx
59 	 '?', 0xa4, 0x95, 0xa2, 0x93,  '?', 0x94,  '?',  '?', 0x97, 0xa3, 0x96, 0x81,  '?',  '?', 0x98  // 0xFx
60 };
61 
62 struct SciKeyConversion {
63 	Common::KeyCode scummVMKey;
64 	int sciKeyNumlockOff;
65 	int sciKeyNumlockOn;
66 };
67 
68 // Translation table for UTF16->Win1250 (Polish encoding)
69 // Covers characters 0x80-0xFF.
70 // '0x0' means end of list, 0x1 means 'unused'
71 static const uint16 UTF16toWin1250[] = {
72 	0x20ac, 0x0001, 0x201a, 0x0001, 0x201e, 0x2026, 0x2020, 0x2021,	// 0x80
73 	0x0001, 0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179,
74 	0x0001, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,	// 0x90
75 	0x0001, 0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a,
76 	0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7,	// 0xa0
77 	0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b,
78 	0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7,	// 0xb0
79 	0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c,
80 	0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7,	// 0xc0
81 	0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e,
82 	0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7,	// 0xd0
83 	0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df,
84 	0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7,	// 0xe0
85 	0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f,
86 	0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7,	// 0xf0
87 	0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9,
88 	0
89 };
90 
91 // Table for transferring Alt+<Key> into Polish symbols
92 // Note, that 'A' and 'S' are swapped with lowercase. This is due to the fact
93 // that we use Alt+a for aspect ratio correction and Alt+s for screenshot saving
94 // and we do that in the SDL backend. There are no words starting with 'A' and
95 // very few starting with S, thus, this is a viable compromise
96 static const byte PolishAltifytoWin1250[] = {
97 	'e', 0xea, 'o', 0xf3, 'a', 0xa5, 's', 0x8c, 'l', 0xb3, 'z', 0xbf, 'x', 0x9f, 'c', 0xe6, 'n', 0xF1,
98 	'E', 0xca, 'O', 0xd3, 'A', 0xb9, 'S', 0x9c, 'L', 0xa3, 'Z', 0xaf, 'X', 0x8f, 'C', 0xc6, 'N', 0xD1,
99 	0
100 };
101 
102 static const SciKeyConversion keyMappings[] = {
103 	{ Common::KEYCODE_UP          , kSciKeyUp       , kSciKeyUp       },
104 	{ Common::KEYCODE_DOWN        , kSciKeyDown     , kSciKeyDown     },
105 	{ Common::KEYCODE_RIGHT       , kSciKeyRight    , kSciKeyRight    },
106 	{ Common::KEYCODE_LEFT        , kSciKeyLeft     , kSciKeyLeft     },
107 	{ Common::KEYCODE_INSERT      , kSciKeyInsert   , kSciKeyInsert   },
108 	{ Common::KEYCODE_HOME        , kSciKeyHome     , kSciKeyHome     },
109 	{ Common::KEYCODE_END         , kSciKeyEnd      , kSciKeyEnd      },
110 	{ Common::KEYCODE_PAGEUP      , kSciKeyPageUp   , kSciKeyPageUp   },
111 	{ Common::KEYCODE_PAGEDOWN    , kSciKeyPageDown , kSciKeyPageDown },
112 	{ Common::KEYCODE_DELETE      , kSciKeyDelete   , kSciKeyDelete   },
113 	{ Common::KEYCODE_KP0         , kSciKeyInsert   , '0'             },
114 	{ Common::KEYCODE_KP1         , kSciKeyEnd      , '1'             },
115 	{ Common::KEYCODE_KP2         , kSciKeyDown     , '2'             },
116 	{ Common::KEYCODE_KP3         , kSciKeyPageDown , '3'             },
117 	{ Common::KEYCODE_KP4         , kSciKeyLeft     , '4'             },
118 	{ Common::KEYCODE_KP5         , kSciKeyCenter   , '5'             },
119 	{ Common::KEYCODE_KP6         , kSciKeyRight    , '6'             },
120 	{ Common::KEYCODE_KP7         , kSciKeyHome     , '7'             },
121 	{ Common::KEYCODE_KP8         , kSciKeyUp       , '8'             },
122 	{ Common::KEYCODE_KP9         , kSciKeyPageUp   , '9'             },
123 	{ Common::KEYCODE_KP_PERIOD   , kSciKeyDelete   , '.'             },
124 	{ Common::KEYCODE_KP_ENTER    , kSciKeyEnter    , kSciKeyEnter    },
125 	{ Common::KEYCODE_KP_PLUS     , '+'             , '+'             },
126 	{ Common::KEYCODE_KP_MINUS    , '-'             , '-'             },
127 	{ Common::KEYCODE_KP_MULTIPLY , '*'             , '*'             },
128 	{ Common::KEYCODE_KP_DIVIDE   , '/'             , '/'             }
129 };
130 
131 struct MouseEventConversion {
132 	Common::EventType commonType;
133 	SciEventType sciType;
134 };
135 
136 static const MouseEventConversion mouseEventMappings[] = {
137 	{ Common::EVENT_LBUTTONDOWN , kSciEventMousePress   },
138 	{ Common::EVENT_RBUTTONDOWN , kSciEventMousePress   },
139 	{ Common::EVENT_MBUTTONDOWN , kSciEventMousePress   },
140 	{ Common::EVENT_LBUTTONUP   , kSciEventMouseRelease },
141 	{ Common::EVENT_RBUTTONUP   , kSciEventMouseRelease },
142 	{ Common::EVENT_MBUTTONUP   , kSciEventMouseRelease }
143 };
144 
EventManager(bool fontIsExtended)145 EventManager::EventManager(bool fontIsExtended) :
146 	_fontIsExtended(fontIsExtended)
147 #ifdef ENABLE_SCI32
148 	, _hotRectanglesActive(false)
149 #endif
150 	{}
151 
~EventManager()152 EventManager::~EventManager() {
153 }
154 
155 /**
156  * Calculates the IBM keyboard alt-key scancode of a printable character.
157  */
altify(char ch)158 static int altify(char ch) {
159 	if (g_sci->getLanguage() == Common::PL_POL) {
160 		const byte *p = PolishAltifytoWin1250;
161 
162 		while (*p) {
163 			if ((byte)ch == *p)
164 				return *(p + 1);
165 
166 			p += 2;
167 		}
168 	}
169 
170 	const char c = toupper(ch);
171 
172 	for (int row = 0; row < ARRAYSIZE(scancodeAltifyRows); ++row) {
173 		const char *keys = scancodeAltifyRows[row].keys;
174 		int offset = scancodeAltifyRows[row].offset;
175 
176 		while (*keys) {
177 			if (*keys == c)
178 				return offset << 8;
179 
180 			++offset;
181 			++keys;
182 		}
183 	}
184 
185 	return ch;
186 }
187 
getScummVMEvent()188 SciEvent EventManager::getScummVMEvent() {
189 #ifdef ENABLE_SCI32
190 	SciEvent input   = { kSciEventNone, kSciKeyModNone, 0, Common::Point(), Common::Point(), -1 };
191 	SciEvent noEvent = { kSciEventNone, kSciKeyModNone, 0, Common::Point(), Common::Point(), -1 };
192 #else
193 	SciEvent input   = { kSciEventNone, kSciKeyModNone, 0, Common::Point() };
194 	SciEvent noEvent = { kSciEventNone, kSciKeyModNone, 0, Common::Point() };
195 #endif
196 
197 	Common::EventManager *em = g_system->getEventManager();
198 	Common::Event ev;
199 
200 	// SCI does not generate separate events for mouse movement (it puts the
201 	// current mouse position on every event, including non-mouse events), so
202 	// skip past all mousemove events in the event queue
203 	bool found;
204 	do {
205 		found = em->pollEvent(ev);
206 	} while (found && ev.type == Common::EVENT_MOUSEMOVE);
207 
208 	Common::Point mousePos = em->getMousePos();
209 
210 #if ENABLE_SCI32
211 	if (getSciVersion() >= SCI_VERSION_2) {
212 		const GfxFrameout *gfxFrameout = g_sci->_gfxFrameout;
213 
214 		// This will clamp `mousePos` according to the restricted zone,
215 		// so any cursor or screen item associated with the mouse position
216 		// does not bounce when it hits the edge (or ignore the edge)
217 		g_sci->_gfxCursor32->deviceMoved(mousePos);
218 
219 		Common::Point mousePosSci = mousePos;
220 		mulru(mousePosSci, Ratio(gfxFrameout->getScriptWidth(), gfxFrameout->getScreenWidth()), Ratio(gfxFrameout->getScriptHeight(), gfxFrameout->getScreenHeight()));
221 		noEvent.mousePosSci = input.mousePosSci = mousePosSci;
222 
223 		if (_hotRectanglesActive) {
224 			checkHotRectangles(mousePosSci);
225 		}
226 	} else {
227 #endif
228 		g_sci->_gfxScreen->adjustBackUpscaledCoordinates(mousePos.y, mousePos.x);
229 #if ENABLE_SCI32
230 	}
231 #endif
232 
233 	noEvent.mousePos = input.mousePos = mousePos;
234 
235 	if (!found || ev.type == Common::EVENT_MOUSEMOVE) {
236 		int modifiers = em->getModifierState();
237 		if (modifiers & Common::KBD_ALT)
238 			noEvent.modifiers |= kSciKeyModAlt;
239 		if (modifiers & Common::KBD_CTRL)
240 			noEvent.modifiers |= kSciKeyModCtrl;
241 		if (modifiers & Common::KBD_SHIFT)
242 			noEvent.modifiers |= kSciKeyModShift;
243 
244 		return noEvent;
245 	}
246 	if (ev.type == Common::EVENT_QUIT || ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
247 		input.type = kSciEventQuit;
248 		return input;
249 	}
250 
251 	int scummVMKeyFlags;
252 
253 	switch (ev.type) {
254 	case Common::EVENT_KEYDOWN:
255 	case Common::EVENT_KEYUP:
256 		// Use keyboard modifiers directly in case this is a keyboard event
257 		scummVMKeyFlags = ev.kbd.flags;
258 		break;
259 	default:
260 		// Otherwise get them from EventManager
261 		scummVMKeyFlags = em->getModifierState();
262 		break;
263 	}
264 
265 	// Caps lock and scroll lock are not handled here because we already
266 	// handle upper case keys elsewhere, and scroll lock doesn't seem to
267 	// ever be used
268 	input.modifiers = kSciKeyModNone;
269 	if (scummVMKeyFlags & Common::KBD_ALT)
270 		input.modifiers |= kSciKeyModAlt;
271 	if (scummVMKeyFlags & Common::KBD_CTRL)
272 		input.modifiers |= kSciKeyModCtrl;
273 	if (scummVMKeyFlags & Common::KBD_SHIFT)
274 		input.modifiers |= kSciKeyModShift;
275 
276 	// Handle mouse events
277 	for (int i = 0; i < ARRAYSIZE(mouseEventMappings); i++) {
278 		if (mouseEventMappings[i].commonType == ev.type) {
279 			input.type = mouseEventMappings[i].sciType;
280 			// Sierra passed keyboard modifiers for mouse events, too.
281 
282 			// Sierra also set certain modifiers within their mouse interrupt handler
283 			// This whole thing was probably meant for people using a mouse, that only featured 1 button
284 			// So the user was able to press Ctrl and click the mouse button to create a right click.
285 			switch (ev.type) {
286 			case Common::EVENT_RBUTTONDOWN: // right button
287 			case Common::EVENT_RBUTTONUP:
288 				input.modifiers |= kSciKeyModShift; // this value was hardcoded in the mouse interrupt handler
289 				break;
290 			case Common::EVENT_MBUTTONDOWN: // middle button
291 			case Common::EVENT_MBUTTONUP:
292 				input.modifiers |= kSciKeyModCtrl; // this value was hardcoded in the mouse interrupt handler
293 				break;
294 			default:
295 				break;
296 			}
297 			return input;
298 		}
299 	}
300 
301 	// Handle keyboard events for the rest of the function
302 	if (ev.type != Common::EVENT_KEYDOWN && ev.type != Common::EVENT_KEYUP) {
303 		return noEvent;
304 	}
305 
306 	// The IBM keyboard driver prior to SCI1.1 only sent keydown events to the
307 	// interpreter
308 	if (ev.type != Common::EVENT_KEYDOWN && getSciVersion() < SCI_VERSION_1_1) {
309 		return noEvent;
310 	}
311 
312 	const Common::KeyCode scummVMKeycode = ev.kbd.keycode;
313 
314 	input.character = ev.kbd.ascii;
315 
316 	if (scummVMKeycode >= Common::KEYCODE_KP0 && scummVMKeycode <= Common::KEYCODE_KP9 && !(scummVMKeyFlags & Common::KBD_NUM)) {
317 		// TODO: Leaky abstractions from SDL should not be handled in game
318 		// engines!
319 		// SDL may provide a character value for numpad keys even if numlock is
320 		// turned off, but arrow/navigation keys won't get mapped below if a
321 		// character value is provided
322 		input.character = 0;
323 	}
324 
325 	if (input.character && input.character <= 0xFF) {
326 		// Extended characters need to be converted to the old to DOS CP850/437
327 		// character sets for multilingual games
328 		if (input.character >= 0x80 && input.character <= 0xFF) {
329 			// SSCI accepted all input scan codes, regardless of locale, and
330 			// just didn't display any characters that were missing from fonts
331 			// used by text input controls. We intentionally filter them out
332 			// entirely for non-multilingual games here instead, so we can have
333 			// better error detection for bugs in the text controls
334 			if (!_fontIsExtended) {
335 				return noEvent;
336 			}
337 
338 			input.character = codePageMap88591ToDOS[input.character & 0x7f];
339 		}
340 
341 		if (scummVMKeycode == Common::KEYCODE_TAB) {
342 			input.character = kSciKeyTab;
343 			if (scummVMKeyFlags & Common::KBD_SHIFT)
344 				input.character = kSciKeyShiftTab;
345 		}
346 
347 		if (scummVMKeycode == Common::KEYCODE_DELETE)
348 			input.character = kSciKeyDelete;
349 	} else if (scummVMKeycode >= Common::KEYCODE_F1 && scummVMKeycode <= Common::KEYCODE_F10) {
350 		if (scummVMKeyFlags & Common::KBD_SHIFT)
351 			input.character = kSciKeyShiftF1 + ((scummVMKeycode - Common::KEYCODE_F1) << 8);
352 		else
353 			input.character = kSciKeyF1 + ((scummVMKeycode - Common::KEYCODE_F1) << 8);
354 	} else {
355 		// Arrow keys, numpad keys, etc.
356 		for (int i = 0; i < ARRAYSIZE(keyMappings); i++) {
357 			if (keyMappings[i].scummVMKey == scummVMKeycode) {
358 				const bool numlockOn = (ev.kbd.flags & Common::KBD_NUM);
359 				input.character = numlockOn ? keyMappings[i].sciKeyNumlockOn : keyMappings[i].sciKeyNumlockOff;
360 				break;
361 			}
362 		}
363 
364 		if (g_sci->getLanguage() == Common::RU_RUS) {
365 			// Convert UTF16 to CP866
366 			if (input.character >= 0x400 && input.character <= 0x4ff) {
367 				if (input.character >= 0x440)
368 					input.character = input.character - 0x410 + 0xb0;
369 				else
370 					input.character = input.character - 0x410 + 0x80;
371 			}
372 		} else if (g_sci->getLanguage() == Common::PL_POL) {
373 			for (int i = 0; UTF16toWin1250[i]; i++)
374 				if (UTF16toWin1250[i] == input.character) {
375 					input.character = 0x80 + i;
376 					break;
377 				}
378 		} else if (g_sci->getLanguage() == Common::HE_ISR) {
379 			if (input.character >= 0x05d0 && input.character <= 0x05ea)
380 				// convert to WIN-1255
381 				input.character = input.character - 0x05d0 + 0xe0;
382 		}
383 	}
384 
385 	// TODO: Leaky abstractions from SDL should not be handled in game engines!
386 	// When Ctrl and Alt are pressed together with a printable key, SDL1 on
387 	// Linux will give us a control character instead of the printable
388 	// character we need to convert to an alt scancode
389 	if ((scummVMKeyFlags & Common::KBD_ALT) && input.character > 0 && input.character < 27)
390 		input.character += 96; // 0x01 -> 'a'
391 
392 	if (scummVMKeyFlags & Common::KBD_ALT) {
393 		input.character = altify(input.character & 0xFF);
394 	} else if ((scummVMKeyFlags & Common::KBD_NON_STICKY) == Common::KBD_CTRL && input.character >= 'a' && input.character <= 'z') {
395 		// In SSCI, Ctrl+<key> generates ASCII control characters, but the
396 		// backends usually give us a printable character + Ctrl flag, so
397 		// convert this combo back into what is expected by game scripts
398 		input.character -= 96;
399 	}
400 
401 	// In SCI1.1, if only a modifier key is pressed, the IBM keyboard driver
402 	// sends an event the same as if a key had been released
403 	if (getSciVersion() != SCI_VERSION_1_1 && !input.character) {
404 		return noEvent;
405 	} else if (!input.character || ev.type == Common::EVENT_KEYUP) {
406 		input.type = kSciEventKeyUp;
407 
408 		// SCI32 includes the released key character code in keyup messages, but
409 		// the IBM keyboard driver in SCI1.1 sends a special character value
410 		// instead. This is necessary to prevent at least Island of Dr Brain
411 		// from processing keyup events as though they were keydown events in
412 		// the word search puzzle
413 		if (getSciVersion() == SCI_VERSION_1_1) {
414 			input.character = 0x8000;
415 		}
416 	} else {
417 		input.type = kSciEventKeyDown;
418 	}
419 
420 	return input;
421 }
422 
updateScreen()423 void EventManager::updateScreen() {
424 	// Update the screen here, since it's called very often.
425 	// Throttle the screen update rate to 60fps.
426 	EngineState *s = g_sci->getEngineState();
427 	if (g_system->getMillis() - s->_screenUpdateTime >= 1000 / 60) {
428 		g_system->updateScreen();
429 		s->_screenUpdateTime = g_system->getMillis();
430 		// Throttle the checking of shouldQuit() to 60fps as well, since
431 		// Engine::shouldQuit() invokes 2 virtual functions
432 		// (EventManager::shouldQuit() and EventManager::shouldReturnToLauncher()),
433 		// which is very expensive to invoke constantly without any
434 		// throttling at all.
435 		if (g_engine->shouldQuit())
436 			s->abortScriptProcessing = kAbortQuitGame;
437 	}
438 }
439 
getSciEvent(SciEventType mask)440 SciEvent EventManager::getSciEvent(SciEventType mask) {
441 #ifdef ENABLE_SCI32
442 	SciEvent event = { kSciEventNone, kSciKeyModNone, 0, Common::Point(), Common::Point(), -1 };
443 #else
444 	SciEvent event = { kSciEventNone, kSciKeyModNone, 0, Common::Point() };
445 #endif
446 
447 	if (getSciVersion() < SCI_VERSION_2) {
448 		updateScreen();
449 	}
450 
451 	// Get all queued events from graphics driver
452 	do {
453 		event = getScummVMEvent();
454 		if (event.type != kSciEventNone)
455 			_events.push_back(event);
456 	} while (event.type != kSciEventNone);
457 
458 	// Search for matching event in queue
459 	Common::List<SciEvent>::iterator iter = _events.begin();
460 	while (iter != _events.end() && !(iter->type & mask))
461 		++iter;
462 
463 	if (iter != _events.end()) {
464 		// Event found
465 		event = *iter;
466 
467 		// If not peeking at the queue, remove the event
468 		if (!(mask & kSciEventPeek))
469 			_events.erase(iter);
470 	} else {
471 		// No event found: we must return a kSciEventNone event.
472 
473 		// Because event.type is kSciEventNone already here,
474 		// there is no need to change it.
475 	}
476 
477 	return event;
478 }
479 
flushEvents()480 void EventManager::flushEvents() {
481 	Common::EventManager *em = g_system->getEventManager();
482 	Common::Event event;
483 	while (em->pollEvent(event)) {}
484 	_events.clear();
485 }
486 
487 #ifdef ENABLE_SCI32
setHotRectanglesActive(const bool active)488 void EventManager::setHotRectanglesActive(const bool active) {
489 	_hotRectanglesActive = active;
490 }
491 
setHotRectangles(const Common::Array<Common::Rect> & rects)492 void EventManager::setHotRectangles(const Common::Array<Common::Rect> &rects) {
493 	_hotRects = rects;
494 	_activeRectIndex = -1;
495 }
496 
checkHotRectangles(const Common::Point & mousePosition)497 void EventManager::checkHotRectangles(const Common::Point &mousePosition) {
498 	int lastActiveRectIndex = _activeRectIndex;
499 	_activeRectIndex = -1;
500 
501 	for (int16 i = 0; i < (int16)_hotRects.size(); ++i) {
502 		if (_hotRects[i].contains(mousePosition)) {
503 			_activeRectIndex = i;
504 			if (i != lastActiveRectIndex) {
505 				SciEvent hotRectEvent;
506 				hotRectEvent.type = kSciEventHotRectangle;
507 				hotRectEvent.hotRectangleIndex = i;
508 				_events.push_front(hotRectEvent);
509 				break;
510 			}
511 
512 			lastActiveRectIndex = _activeRectIndex;
513 		}
514 	}
515 
516 	if (lastActiveRectIndex != _activeRectIndex && lastActiveRectIndex != -1) {
517 		_activeRectIndex = -1;
518 		SciEvent hotRectEvent;
519 		hotRectEvent.type = kSciEventHotRectangle;
520 		hotRectEvent.hotRectangleIndex = -1;
521 		_events.push_front(hotRectEvent);
522 	}
523 }
524 #endif
525 
526 } // End of namespace Sci
527