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 // Disable symbol overrides so that we can use system headers.
24 #define FORBIDDEN_SYMBOL_ALLOW_ALL
25 
26 //#define PSP_KB_SHELL	/* Need a hack to properly load the keyboard from the PSP shell */
27 
28 #ifdef PSP_KB_SHELL
29 #define PSP_KB_SHELL_PATH 	"ms0:/psp/game5xx/scummvm-solid/"	/* path to kbd.zip */
30 #endif
31 
32 
33 #include <malloc.h>
34 #include <pspkernel.h>
35 
36 #include "backends/platform/psp/psppixelformat.h"
37 #include "backends/platform/psp/pspkeyboard.h"
38 #include "backends/platform/psp/png_loader.h"
39 #include "backends/platform/psp/input.h"
40 #include "common/keyboard.h"
41 #include "common/fs.h"
42 #include "common/unzip.h"
43 
44 //#define __PSP_DEBUG_FUNCS__	/* For debugging the stack */
45 //#define __PSP_DEBUG_PRINT__
46 
47 #include "backends/platform/psp/trace.h"
48 
49 #define PSP_SCREEN_WIDTH 480
50 #define PSP_SCREEN_HEIGHT 272
51 #define K(x)	((short)(Common::KEYCODE_INVALID + (x)))
52 #define C(x)	((short)(Common::KEYCODE_##x))
53 
54 // Layout of the keyboard: Order for the boxes is clockwise and then middle:
55 //			1
56 //		4	5	2
57 //			3
58 // and order of letters is clockwise in each box, plus 2 top letters:
59 //		e		f
60 //			a
61 //		d		b
62 //			c
63 // K(x) is used for ascii values. C(x) is used for keys without ascii values
64 short PSPKeyboard::_modeChar[MODE_COUNT][5][6] = {
65 	{	//standard letters
66 		{ K('a'),  K('b'), K('c'), K('d'), K('f'), K('g') },
67 		{ K('h'),  K('i'), K('l'), K('m'), K('j'), K('k') },
68 		{ K('o'),  K('n'), K('r'), K('s'), K('p'), K('q') },
69 		{ K('u'),  K('v'), K('w'), K('y'), K('x'), K('z') },
70 		{ K('\b'), K('t'), K(' '), K('e'), K(0),   K(0)   }
71 	},
72 	{	//capital letters
73 		{ K('A'),  K('B'), K('C'), K('D'), K('F'), K('G') },
74 		{ K('H'),  K('I'), K('L'), K('M'), K('J'), K('K') },
75 		{ K('O'),  K('N'), K('R'), K('S'), K('P'), K('Q') },
76 		{ K('U'),  K('V'), K('W'), K('Y'), K('X'), K('Z') },
77 		{ K('\b'), K('T'), K(' '), K('E'), K(0),   K(0)   }
78 	},
79 	{	//numbers
80 		{ K('1'),  K('2'), K('3'), K('4'), K(0),   K(0)   },
81 		{ C(F5),   C(F6),  C(F7),  C(F8),  C(F9),  C(F10) },
82 		{ K('5'),  K('6'), K('7'), K('8'), K(0),   K(0)   },
83 		{ C(F1),   C(F2),  C(F3),  C(F4),  K(0),   K(0)   },
84 		{ K('\b'), K('0'), K(' '), K('9'), K(0),   K(0)   }
85 	},
86 	{	//symbols
87 		{ K('!'),  K(')'), K('?'), K('('), K('<'), K('>') },
88 		{ K('+'),  K('/'), K('='), K('\\'), K('\''), K('"') },
89 		{ K(':'),  K(']'), K(';'), K('['), K('@'), K('#') },
90 		{ K('-'),  K('}'), K('_'), K('{'), K('*'), K('$') },
91 		{ K('\b'), K('.'), K(' '), K(','), K(0),   K(0)   }
92 	}
93 };
94 
95 // Array with file names
96 const char *PSPKeyboard::_guiStrings[] = {
97 	"keys4.png", "keys_s4.png",
98 	"keys_c4.png", "keys_s_c4.png",
99 	"nums4.png", "nums_s4.png",
100 	"syms4.png", "syms_s4.png"
101 };
102 
103 // Constructor
PSPKeyboard()104 PSPKeyboard::PSPKeyboard() {
105 	DEBUG_ENTER_FUNC();
106 
107 	_init = false;			// we're not initialized yet
108 	_prevButtons = 0;		// Reset previous buttons
109 	_dirty = false;        	// keyboard needs redrawing
110 	_mode = 0;              // charset selected. (0: letters, 1: uppercase 2: numbers 3: symbols)
111 	_oldCursor = kCenter;	// Center cursor by default
112 	_movedX = 20;			// Default starting location
113 	_movedY = 50;
114 	_moved = false;			// Keyboard wasn't moved recently
115 	_state = kInvisible;	// We start invisible
116 	_lastState = kInvisible;
117 
118 	// Constant renderer settings
119 	_renderer.setAlphaBlending(true);
120 	_renderer.setColorTest(false);
121 	_renderer.setUseGlobalScaler(false);
122 }
123 
124 // Destructor
~PSPKeyboard()125 PSPKeyboard::~PSPKeyboard() {
126 	DEBUG_ENTER_FUNC();
127 
128 	if (!_init) {
129 		return;
130 	}
131 
132 	for (int i = 0; i < guiStringsSize; i++) {
133 		_buffers[i].deallocate();
134 		_palettes[i].deallocate();
135 	}
136 	_init = false;
137 }
138 
setVisible(bool val)139 void PSPKeyboard::setVisible(bool val) {
140 	if (val && _state == kInvisible && _init) {	// Check also that were loaded correctly
141 		_lastState = _state;
142 		_state = kMove;
143 	} else if (!val && _state != kInvisible) {
144 		_lastState = _state;
145 		_state = kInvisible;
146 	}
147 	setDirty();
148 }
149 
150 /* move the position the keyboard is currently drawn at */
moveTo(const int newX,const int newY)151 void PSPKeyboard::moveTo(const int newX, const int newY) {
152 	DEBUG_ENTER_FUNC();
153 
154 	_movedX = newX;
155 	_movedY = newY;
156 	setDirty();
157 }
158 
159 /* move the position the keyboard is currently drawn at */
increaseKeyboardLocationX(int amount)160 void PSPKeyboard::increaseKeyboardLocationX(int amount) {
161 	DEBUG_ENTER_FUNC();
162 
163 	int newX = _movedX + amount;
164 
165 	if (newX > PSP_SCREEN_WIDTH - 5 || newX < 0 - 140) {	// clamp
166 		return;
167 	}
168 	_movedX = newX;
169 	setDirty();
170 }
171 
172 /* move the position the keyboard is currently drawn at */
increaseKeyboardLocationY(int amount)173 void PSPKeyboard::increaseKeyboardLocationY(int amount) {
174 	DEBUG_ENTER_FUNC();
175 
176 	int newY = _movedY + amount;
177 
178 	if (newY > PSP_SCREEN_HEIGHT - 5 || newY < 0 - 140)	{ // clamp
179 		return;
180 	}
181 	_movedY = newY;
182 	setDirty();
183 }
184 
185 /* draw the keyboard at the current position */
render()186 void PSPKeyboard::render() {
187 	DEBUG_ENTER_FUNC();
188 
189 	unsigned int currentBuffer = _mode << 1;
190 
191 	// Draw the background letters
192 	// Set renderer to current buffer & palette
193 	_renderer.setBuffer(&_buffers[currentBuffer]);
194 	_renderer.setPalette(&_palettes[currentBuffer]);
195 	_renderer.setOffsetOnScreen(_movedX, _movedY);
196 	_renderer.setOffsetInBuffer(0, 0);
197 	_renderer.setDrawWholeBuffer();
198 	_renderer.render();
199 
200 	// Get X and Y coordinates for the orange block
201 	int x, y;
202 	convertCursorToXY(_oldCursor, x, y);
203 
204 	const int OrangeBlockSize = 64;
205 	const int GrayBlockSize = 43;
206 
207 	// Draw the current Highlighted Selector (orange block)
208 	_renderer.setBuffer(&_buffers[currentBuffer + 1]);
209 	_renderer.setPalette(&_palettes[currentBuffer + 1]);
210 	_renderer.setOffsetOnScreen(_movedX + (x * GrayBlockSize), _movedY + (y * GrayBlockSize));
211 	_renderer.setOffsetInBuffer(x * OrangeBlockSize, y * OrangeBlockSize);
212 	_renderer.setDrawSize(OrangeBlockSize, OrangeBlockSize);
213 	_renderer.render();
214 }
215 
convertCursorToXY(CursorDirections cur,int & x,int & y)216 inline void PSPKeyboard::convertCursorToXY(CursorDirections cur, int &x, int &y) {
217 	switch (cur) {
218 	case kUp:
219 		x = 1;
220 		y = 0;
221 		break;
222 	case kRight:
223 		x = 2;
224 		y = 1;
225 		break;
226 	case kDown:
227 		x = 1;
228 		y = 2;
229 		break;
230 	case kLeft:
231 		x = 0;
232 		y = 1;
233 		break;
234 	default:
235 		x = 1;
236 		y = 1;
237 		break;
238 	}
239 }
240 
241 /* load the keyboard into memory */
load()242 bool PSPKeyboard::load() {
243 	DEBUG_ENTER_FUNC();
244 
245 	if (_init) {
246 		PSP_DEBUG_PRINT("keyboard already loaded into memory\n");
247 		return true;
248 	}
249 
250 	// For the shell, we must use a hack
251 #ifdef PSP_KB_SHELL
252 	Common::FSNode node(PSP_KB_SHELL_PATH);
253 #else /* normal mode */
254 	Common::FSNode node(".");				// Look in current directory
255 #endif
256 	PSP_DEBUG_PRINT("path[%s]\n", node.getPath().c_str());
257 
258 	Common::Archive *fileArchive = NULL;
259 	Common::Archive *zipArchive = NULL;
260 	Common::SeekableReadStream * file = 0;
261 
262 	if (node.getChild("kbd").exists() && node.getChild("kbd").isDirectory()) {
263 		PSP_DEBUG_PRINT("found directory ./kbd\n");
264 		fileArchive = new Common::FSDirectory(node.getChild("kbd"));
265 	}
266 	if (node.getChild("kbd.zip").exists()) {
267 		PSP_DEBUG_PRINT("found kbd.zip\n");
268 		zipArchive  = Common::makeZipArchive(node.getChild("kbd.zip"));
269 	}
270 
271 	int i;
272 
273 	// Loop through all png images
274 	for (i = 0; i < guiStringsSize; i++) {
275 		PSP_DEBUG_PRINT("Opening %s.\n", _guiStrings[i]);
276 
277 		// Look for the file in the kbd directory
278 		if (fileArchive && fileArchive->hasFile(_guiStrings[i])) {
279 			PSP_DEBUG_PRINT("found it in kbd directory.\n");
280 
281 			file = fileArchive->createReadStreamForMember(_guiStrings[i]);
282 			if (!file) {
283 				PSP_ERROR("Can't open kbd/%s for keyboard. No keyboard will load.\n", _guiStrings[i]);
284 				goto ERROR;
285 			}
286 		}
287 		// We didn't find it. Look for it in the zip file
288 		else if (zipArchive && zipArchive->hasFile(_guiStrings[i])) {
289 			PSP_DEBUG_PRINT("found it in kbd.zip.\n");
290 
291 			file = zipArchive->createReadStreamForMember(_guiStrings[i]);
292 			if (!file) {
293 				PSP_ERROR("Can't open %s in kbd.zip for keyboard. No keyboard will load.\n", _guiStrings[i]);
294 				goto ERROR;
295 			}
296 		} else {	// Couldn't find the file
297 			PSP_ERROR("Can't find %s for keyboard. No keyboard will load.\n", _guiStrings[i]);
298 			goto ERROR;
299 		}
300 
301 		PngLoader image(*file, _buffers[i], _palettes[i]);
302 
303 		if (image.allocate() != PngLoader::OK) {
304 			PSP_ERROR("Failed to allocate memory for keyboard image %s\n", _guiStrings[i]);
305 			goto ERROR;
306 		}
307 		if (!image.load()) {
308 			PSP_ERROR("Failed to load image from file %s\n", _guiStrings[i]);
309 			goto ERROR;
310 		}
311 
312 		delete file;
313 	} /* for loop */
314 
315 	_init = true;
316 
317 	delete fileArchive;
318 	delete zipArchive;
319 
320 	return true;
321 
322 ERROR:
323 
324 	delete file;
325 	delete fileArchive;
326 	delete zipArchive;
327 
328 	for (int j = 0; j < i; j++) {
329 		_buffers[j].deallocate();
330 		_palettes[j].deallocate();
331 	}
332 	_init = false;
333 
334 	return false;
335 }
336 
337 // Defines for working with PSP buttons
338 #define CHANGED(x)	 (_buttonsChanged & (x))
339 #define PRESSED(x)   ((_buttonsChanged & (x)) && (pad.Buttons & (x)))
340 #define UNPRESSED(x) ((_buttonsChanged & (x)) && !(pad.Buttons & (x)))
341 #define DOWN(x)		 (pad.Buttons & (x))
342 #define UP(x)		 (!(pad.Buttons & (x)))
343 #define PSP_DPAD	 (PSP_CTRL_DOWN|PSP_CTRL_UP|PSP_CTRL_LEFT|PSP_CTRL_RIGHT)
344 #define PSP_4BUTTONS (PSP_CTRL_CROSS | PSP_CTRL_CIRCLE | PSP_CTRL_TRIANGLE | PSP_CTRL_SQUARE)
345 
346 /*
347  *  Attempts to read a character from the controller
348  *  Uses the state machine.
349  *  returns whether we have an event
350  */
processInput(Common::Event & event,PspEvent & pspEvent,SceCtrlData & pad)351 bool PSPKeyboard::processInput(Common::Event &event, PspEvent &pspEvent, SceCtrlData &pad) {
352 	DEBUG_ENTER_FUNC();
353 
354 	bool haveEvent = false;		// Whether we have an event for the event manager to process
355 	bool havePspEvent = false;
356 	event.kbd.flags = 0;
357 
358 	_buttonsChanged = _prevButtons ^ pad.Buttons;
359 
360 	if (!_init)					// In case we never had init
361 		return false;
362 	if (_state == kInvisible)	// Return if we're invisible
363 		return false;
364 	if (_state != kMove && PRESSED(PSP_CTRL_SELECT)) {
365 		_lastState = _state;
366 		_state = kMove;			// Check for move or visible state
367 	} else if (CHANGED(PSP_CTRL_START)) {		// Handle start button: enter, make KB invisible
368 		event.kbd.ascii = '\r';
369 		event.kbd.keycode = Common::KEYCODE_RETURN;
370 		event.type = DOWN(PSP_CTRL_START) ? Common::EVENT_KEYDOWN : Common::EVENT_KEYUP;
371 		haveEvent = true;
372 		_dirty = true;
373 		if (UP(PSP_CTRL_START))
374 			havePspEvent = true;
375 	}
376 	// Check for being in state of moving the keyboard onscreen or pressing select
377 	else if (_state == kMove)
378 		havePspEvent = handleMoveState(pad);
379 	else if (_state == kDefault)
380 		haveEvent = handleDefaultState(event, pad);
381 	else if (_state == kCornersSelected)
382 		haveEvent = handleCornersSelectedState(event, pad);
383 	else if (_state == kRTriggerDown)
384 		handleRTriggerDownState(pad);	// Deal with trigger states
385 	else if (_state == kLTriggerDown)
386 		handleLTriggerDownState(pad);	// Deal with trigger states
387 
388 	if (havePspEvent) {
389 		pspEvent.type = PSP_EVENT_SHOW_VIRTUAL_KB;	// tell the input handler we're off
390 		pspEvent.data = false;
391 	}
392 	_prevButtons = pad.Buttons;
393 
394 	return haveEvent;
395 }
396 
handleMoveState(SceCtrlData & pad)397 bool PSPKeyboard::handleMoveState(SceCtrlData &pad) {
398 	DEBUG_ENTER_FUNC();
399 	if (UP(PSP_CTRL_SELECT)) {
400 		// Toggle between visible and invisible
401 		_state = (_lastState == kInvisible) ? kDefault : kInvisible;
402 		_dirty = true;
403 
404 		if (_moved) {					// We moved the keyboard. Keep the keyboard onscreen anyway
405 			_state = kDefault;
406 			_moved = false;				// reset moved flag
407 		}
408 		if (_state == kInvisible) {
409 			return true;				// we become invisible
410 		}
411 	} else if (DOWN(PSP_DPAD)) {		// How we move the KB onscreen
412 		_moved = true;
413 		_dirty = true;
414 
415 		if (DOWN(PSP_CTRL_DOWN))
416 			increaseKeyboardLocationY(5);
417 		else if (DOWN(PSP_CTRL_UP))
418 			increaseKeyboardLocationY(-5);
419 		else if (DOWN(PSP_CTRL_LEFT))
420 			increaseKeyboardLocationX(-5);
421 		else  /* DOWN(PSP_CTRL_RIGHT) */
422 			increaseKeyboardLocationX(5);
423 	}
424 	return false;
425 }
426 
handleDefaultState(Common::Event & event,SceCtrlData & pad)427 bool PSPKeyboard::handleDefaultState(Common::Event &event, SceCtrlData &pad) {
428 	DEBUG_ENTER_FUNC();
429 	bool haveEvent = false;
430 
431 	if (PRESSED(PSP_CTRL_LTRIGGER)) 			// Don't say we used up the input
432 		_state = kLTriggerDown;
433 	else if (PRESSED(PSP_CTRL_RTRIGGER)) 	// Don't say we used up the input
434 		_state = kRTriggerDown;
435 	else if (CHANGED(PSP_4BUTTONS))			// We only care about the 4 buttons
436 		haveEvent = getInputChoice(event, pad);
437 	else if (!DOWN(PSP_4BUTTONS))				// Must be up to move cursor
438 		getCursorMovement(pad);
439 
440 	return haveEvent;
441 }
442 
handleCornersSelectedState(Common::Event & event,SceCtrlData & pad)443 bool PSPKeyboard::handleCornersSelectedState(Common::Event &event, SceCtrlData &pad) {
444 	DEBUG_ENTER_FUNC();
445 	// We care about 4 buttons + triggers (for letter selection)
446 	bool haveEvent = false;
447 
448 	if (CHANGED(PSP_4BUTTONS | PSP_CTRL_RTRIGGER | PSP_CTRL_LTRIGGER))
449 		haveEvent = getInputChoice(event, pad);
450 	if (!DOWN(PSP_4BUTTONS | PSP_CTRL_RTRIGGER | PSP_CTRL_LTRIGGER)) // Must be up to move cursor
451 		getCursorMovement(pad);
452 
453 	return haveEvent;
454 }
455 
getInputChoice(Common::Event & event,SceCtrlData & pad)456 bool PSPKeyboard::getInputChoice(Common::Event &event, SceCtrlData &pad) {
457 	DEBUG_ENTER_FUNC();
458 	int innerChoice;
459 	bool haveEvent = false;
460 
461 	if (UNPRESSED(PSP_CTRL_TRIANGLE)) {
462 		innerChoice = 0;
463 		event.type = Common::EVENT_KEYUP;			// We give priority to key_up
464 	} else if (UNPRESSED(PSP_CTRL_CIRCLE)) {
465 		innerChoice = 1;
466 		event.type = Common::EVENT_KEYUP;			// We give priority to key_up
467 	} else if (UNPRESSED(PSP_CTRL_CROSS)) {
468 		innerChoice = 2;
469 		event.type = Common::EVENT_KEYUP;			// We give priority to key_up
470 	} else if (UNPRESSED(PSP_CTRL_SQUARE)) {
471 		innerChoice = 3;
472 		event.type = Common::EVENT_KEYUP;			// We give priority to key_up
473 	} else if (UNPRESSED(PSP_CTRL_LTRIGGER) && _state == kCornersSelected) {
474 		innerChoice = 4;
475 		event.type = Common::EVENT_KEYUP;			// We give priority to key_up
476 	} else if (UNPRESSED(PSP_CTRL_RTRIGGER) && _state == kCornersSelected) {
477 		innerChoice = 5;
478 		event.type = Common::EVENT_KEYUP;			// We give priority to key_up
479 	} else if (PRESSED(PSP_CTRL_TRIANGLE)) {
480 		innerChoice = 0;
481 		event.type = Common::EVENT_KEYDOWN;
482 	} else if (PRESSED(PSP_CTRL_CIRCLE)) {
483 		innerChoice = 1;
484 		event.type = Common::EVENT_KEYDOWN;
485 	} else if (PRESSED(PSP_CTRL_CROSS)) {
486 		innerChoice = 2;
487 		event.type = Common::EVENT_KEYDOWN;
488 	} else if (PRESSED(PSP_CTRL_SQUARE)) {
489 		innerChoice = 3;
490 		event.type = Common::EVENT_KEYDOWN;
491 	} else if (PRESSED(PSP_CTRL_LTRIGGER) && _state == kCornersSelected) {
492 		innerChoice = 4;
493 		event.type = Common::EVENT_KEYDOWN;			// We give priority to key_up
494 	} else { /* (PRESSED(PSP_CTRL_RTRIGGER)) && _state == kCornersSelected */
495 		innerChoice = 5;
496 		event.type = Common::EVENT_KEYDOWN;			// We give priority to key_up
497 	}
498 
499 #define IS_UPPERCASE(x)	((x) >= (unsigned short)'A' && (x) <= (unsigned short)'Z')
500 #define TO_LOWER(x)		((x) += 'a'-'A')
501 
502 	//Now grab the value out of the array
503 	short choice = _modeChar[_mode][_oldCursor][innerChoice];
504 
505 	event.kbd.ascii = choice <= 255 ? choice : 0;
506 
507 	// Handle upper-case which is missing in Common::KeyCode
508 	if (IS_UPPERCASE(choice)) {
509 		event.kbd.keycode = (Common::KeyCode) TO_LOWER(choice);
510 		event.kbd.flags = Common::KBD_SHIFT;
511 	} else
512 		event.kbd.keycode = (Common::KeyCode) choice;
513 
514 	haveEvent = (choice != Common::KEYCODE_INVALID) ? true : false;	// We have an event/don't if it's invalid
515 
516 	return haveEvent;
517 }
518 
getCursorMovement(SceCtrlData & pad)519 void PSPKeyboard::getCursorMovement(SceCtrlData &pad) {
520 	DEBUG_ENTER_FUNC();
521 	CursorDirections cursor;
522 
523 	// Find where the cursor is pointing
524 	cursor = kCenter;
525 	_state = kDefault;
526 
527 	if (DOWN(PSP_DPAD)) {
528 		_state = kCornersSelected;
529 
530 		if (DOWN(PSP_CTRL_UP))
531 			cursor = kUp;
532 		else if (DOWN(PSP_CTRL_RIGHT))
533 			cursor = kRight;
534 		else if (DOWN(PSP_CTRL_DOWN))
535 			cursor = kDown;
536 		else if (DOWN(PSP_CTRL_LEFT))
537 			cursor = kLeft;
538 	}
539 
540 	if (cursor != _oldCursor) { //If we've moved, update dirty and return
541 		_dirty = true;
542 		_oldCursor = cursor;
543 	}
544 }
545 
handleLTriggerDownState(SceCtrlData & pad)546 void PSPKeyboard::handleLTriggerDownState(SceCtrlData &pad) {
547 	DEBUG_ENTER_FUNC();
548 	if (UNPRESSED(PSP_CTRL_LTRIGGER)) {
549 		_dirty = true;
550 
551 		if (_mode < 2)
552 			_mode = 2;
553 		else
554 			_mode = (_mode == 2) ? 3 : 2;
555 
556 		_state = kDefault;
557 	}
558 }
559 
handleRTriggerDownState(SceCtrlData & pad)560 void PSPKeyboard::handleRTriggerDownState(SceCtrlData &pad) {
561 	DEBUG_ENTER_FUNC();
562 	if (UNPRESSED(PSP_CTRL_RTRIGGER)) {
563 		_dirty = true;
564 
565 		if (_mode > 1)
566 			_mode = 0;
567 		else
568 			_mode = (_mode == 0) ? 1 : 0;
569 
570 		_state = kDefault;
571 	}
572 }
573