1 /*
2  * PROPRIETARY INFORMATION.  This software is proprietary to POWDER
3  * Development, and is not to be reproduced, transmitted, or disclosed
4  * in any way without written permission.
5  *
6  * Produced by:	Jeff Lait
7  *
8  *      	POWDER Development
9  *
10  * NAME:        input.cpp ( POWDER Library, C++ )
11  *
12  * COMMENTS:
13  *	Routines to accept input...
14  */
15 
16 #include "input.h"
17 
18 #include "assert.h"
19 #include "gfxengine.h"
20 #include "input.h"
21 #include "control.h"
22 #include "rand.h"
23 #include "stylus.h"
24 
25 #define KEYWIDTH 14
26 #define KEYHEIGHT 5
27 #define KEYTOP 6
28 #define KEYLEFT 1
29 
30 #ifdef USING_SDL
31 #define TILEWIDTH (gfx_gettilewidth())
32 #define TILEHEIGHT (gfx_gettileheight())
33 #else
34 #define TILEWIDTH 8
35 #define TILEHEIGHT 8
36 #endif
37 
38 
39 // Given the direction, this determines the character...
40 char
input_getstatechar(int dx,int dy,bool shifted)41 input_getstatechar(int dx, int dy, bool shifted)
42 {
43     int			code = 0;
44     int			bits;
45 
46     UT_ASSERT(dx || dy);
47 
48     if (dx)
49 	code = (dx < 0) ? 0 : 1;
50     else
51 	code = (dy < 0) ? 2 : 3;
52 
53     // Now, we could just have a bit field for the additional buttons,
54     // but that would make priority calculations difficult.  Instead,
55     // we go:
56     //      , A,   B,   L,    | R,   L+R,   A+L,   A+R,
57     // A+L+R, B+L, B+R, B+L+R,| A+B, A+B+L, A+B+R, A+B+L+R
58     static const int	bitlut[16] =
59     //     , A,   B,   A+B,   L,   L+A,   L+B,   L+A+B,
60 	{ 0, 1,   2,   12,    3,   6,     9,     13,
61     //    R, R+A, R+B, R+A+B, R+L, R+L+A, R+L+B, R+L+B+A
62 	  4, 7,   10,  14,    5,   8,     11,    15 };
63 
64     // 65 so we have the null terminator.
65     static const char charlut[65] =
66 	"\b \t\n"
67 	"abcd" "efgh" "ijkl" "mnop" "qrst" "uvwx" "yz'\""
68 	"0123" "4567" "89!#"
69 	"+-/*" "|_^&"
70 	"()[]" "{}<>"
71 	".,:;";
72 
73     static const char charlutshift[65] =
74 	"\b \t\n"
75 	"ABCD" "EFGH" "IJKL" "MNOP" "QRST" "UVWX" "YZ'\""
76 	"0123" "4567" "89!#"
77 	"+-/*" "|_^&"
78 	"()[]" "{}<>"
79 	".,:;";
80 
81     bits = (ctrl_rawpressed(BUTTON_A)) |
82 	   (ctrl_rawpressed(BUTTON_B) << 1) |
83 	   (ctrl_rawpressed(BUTTON_L) << 2) |
84 	   (ctrl_rawpressed(BUTTON_R) << 3);
85 
86     // Mix in the X & Y so DS users have it even easier.
87     bits |= (ctrl_rawpressed(BUTTON_X) << 3);
88     bits |= (ctrl_rawpressed(BUTTON_Y) << 2);
89 
90     code = code | (bitlut[bits] << 2);
91 
92     if (shifted)
93 	return charlutshift[code];
94     else
95 	return charlut[code];
96 }
97 
98 void
input_updategnomon(int gx,int gy,bool shifted)99 input_updategnomon(int gx, int gy, bool shifted)
100 {
101     int		dx, dy;
102 
103     for (dy = -1; dy <= 1; dy++)
104     {
105 	for (dx = -1; dx <= 1; dx++)
106 	{
107 	    if ((!dx) ^ (!dy))
108 	    {
109 		gfx_printchar(gx+dx, gy+dy, input_getstatechar(dx, dy, shifted));
110 	    }
111 	}
112     }
113 }
114 
115 const char glbKeyboard[KEYWIDTH * KEYHEIGHT + 1] =
116      "=1234567890-\b\b"
117      " qwertyuiop\\ " SYMBOLSTRING_TRIDUDE
118      " asdfghjkl;' \n"
119      " zxcvbnm,./  \n"
120      "\t`\\ \b   \n []\t ";
121 
122 const char glbKeyboardShift[KEYWIDTH * KEYHEIGHT + 1] =
123      "+!@#$%^&*()_ \b"
124      " QWERTYUIOP| " SYMBOLSTRING_TRIDUDE
125      " ASDFGHJKL:\" \n"
126      " ZXCVBNM<>?  \n"
127      "\t~| \b   \n {}\t ";
128 
129 char
input_lookupkey(int x,int y,bool shifted)130 input_lookupkey(int x, int y, bool shifted)
131 {
132     if (x < 0 || x >= KEYWIDTH)
133 	return 0;
134     if (y < 0 || y >= KEYHEIGHT)
135 	return 0;
136 
137     if (shifted)
138 	return glbKeyboardShift[x + y * KEYWIDTH];
139     else
140 	return glbKeyboard[x + y * KEYWIDTH];
141 }
142 
143 void
input_drawkeyboard(bool shifted)144 input_drawkeyboard(bool shifted)
145 {
146     // GBA has no stylus so gets no keyboard.  They are stuck
147     // with the one true input method.
148     // That is, until the fateful day we decided to abandon
149     // the one true input method.
150     int		x, y;
151     const char	*key;
152 
153     if (shifted)
154 	key = glbKeyboardShift;
155     else
156 	key = glbKeyboard;
157     for (y = 0; y < KEYHEIGHT; y++)
158     {
159 	for (x = 0; x < KEYWIDTH; x++)
160 	{
161 	    gfx_printchar(KEYLEFT+x*2, KEYTOP+y*2, *key);
162 	    key++;
163 
164 	    gfx_setabsoverlay(x, 3+y, TILE_EMPTYSLOT);
165 	}
166     }
167 }
168 
169 void
input_erasecursor(int kx,int ky)170 input_erasecursor(int kx, int ky)
171 {
172     gfx_printchar(kx*2 + KEYLEFT-1, ky*2 + KEYTOP, ' ');
173     gfx_printchar(kx*2 + KEYLEFT+1, ky*2 + KEYTOP, ' ');
174     gfx_printchar(kx*2 + KEYLEFT, ky*2 + KEYTOP-1, ' ');
175     gfx_printchar(kx*2 + KEYLEFT, ky*2 + KEYTOP+1, ' ');
176 }
177 
178 void
input_drawcursor(int kx,int ky)179 input_drawcursor(int kx, int ky)
180 {
181     gfx_printcolourchar(kx*2 + KEYLEFT-1, ky*2 + KEYTOP, SYMBOL_RIGHT, COLOUR_RED);
182     gfx_printcolourchar(kx*2 + KEYLEFT+1, ky*2 + KEYTOP, SYMBOL_LEFT, COLOUR_RED);
183     gfx_printcolourchar(kx*2 + KEYLEFT, ky*2 + KEYTOP-1, SYMBOL_DOWN, COLOUR_RED);
184     gfx_printcolourchar(kx*2 + KEYLEFT, ky*2 + KEYTOP+1, SYMBOL_UP, COLOUR_RED);
185 }
186 
187 int
input_getchar(int gx,int gy,bool shifted)188 input_getchar(int gx, int gy, bool shifted)
189 {
190     STYLUSLOCK		styluslock(REGION_KEYBOARD);
191     int			sx, sy;
192 
193     styluslock.setRange(KEYLEFT, KEYTOP,
194 			KEYLEFT+2*KEYWIDTH-1, KEYTOP+2*KEYHEIGHT-1);
195 
196 #ifdef HAS_KEYBOARD
197     while (1)
198     {
199 	int		key;
200 
201 	// We do a random just to keep things ticking...
202 	rand_choice(3);
203 	hamfake_rebuildScreen();
204 	hamfake_awaitEvent();
205 	key = hamfake_getKeyPress(false);
206 
207 	// Ignore mouse keys as we want the stylus lock to work.
208 	if (key == GFX_KEYLMB || key == GFX_KEYMMB || key == GFX_KEYRMB)
209 	    key = 0;
210 
211 	if (key)
212 	    return key;
213 
214 	// Check stylus.
215 	if (styluslock.getchartile(sx, sy))
216 	{
217 	    key = input_lookupkey(sx, sy, shifted);
218 	    if (key)
219 		return key;
220 	}
221     }
222 #else
223     int		dx, dy, key = 0;
224     // I have no idea what character this is!
225     static int	key_x = 5, key_y = 2;
226 
227     input_drawcursor(key_x, key_y);
228     while (1)
229     {
230 	// We do a random just to keep things ticking...
231 	rand_choice(3);
232 
233 	if (hamfake_forceQuit())
234 	    return '\n';
235 
236 #ifdef USE_VIRTUAL_SCREEN
237 	if (hamfake_hasbeenshaken())
238 	{
239 	    return GFX_KEYF3;
240 	}
241 #endif
242 
243 	hamfake_rebuildScreen();
244 	hamfake_awaitEvent();
245 
246 	ctrl_getdir(dx, dy);
247 	if (dx || dy)
248 	{
249 	    // Move the current key.
250 	    input_erasecursor(key_x, key_y);
251 	    key_x += dx;
252 	    key_y += dy;
253 	    key_x %= KEYWIDTH;
254 	    key_y %= KEYHEIGHT;
255 	    if (key_x < 0)
256 		key_x += KEYWIDTH;
257 	    if (key_y < 0)
258 		key_y += KEYHEIGHT;
259 
260 	    input_drawcursor(key_x, key_y);
261 	}
262 
263 	if (ctrl_hit(BUTTON_A))
264 	{
265 	    key = input_lookupkey(key_x, key_y, shifted);
266 	    if (key)
267 		break;
268 	}
269 
270 	// Other buttons are tied to certain built in keypresses.
271 	if (ctrl_hit(BUTTON_L))
272 	{
273 	    key = '\b';
274 	    break;
275 	}
276 
277 	if (ctrl_hit(BUTTON_R))
278 	{
279 	    key = ' ';
280 	    break;
281 	}
282 
283 	if (ctrl_hit(BUTTON_B))
284 	{
285 	    // This should be an Escape and thus quit without
286 	    // actually saving a line of text, except we don't support
287 	    // that option in general and it is much more useful to be
288 	    // able to hit enter without fiddling with our key position.
289 	    key = '\n';
290 	    break;
291 	}
292 
293 	// Start makes sense to start the game so select your text
294 	if (ctrl_hit(BUTTON_START))
295 	{
296 	    key = '\n';
297 	    break;
298 	}
299 
300 	// Select is for toggling shift - selecting the font?
301 	if (ctrl_hit(BUTTON_SELECT))
302 	{
303 	    key = '\t';
304 	    break;
305 	}
306 
307 	// Check stylus.
308 	if (styluslock.getchartile(sx, sy))
309 	{
310 	    key = input_lookupkey(sx, sy, shifted);
311 	    if (key)
312 	    {
313 		// Also adjust our button press location
314 		input_erasecursor(key_x, key_y);
315 		key_x = sx;
316 		key_y = sy;
317 		break;
318 	    }
319 	}
320     }
321 
322     input_erasecursor(key_x, key_y);
323 
324     return key;
325 #endif
326 }
327 
328 void
input_getline(char * text,int len,int lx,int ly,int gx,int gy,const char * initialtext)329 input_getline(char *text, int len, int lx, int ly, int gx, int gy,
330 		const char *initialtext)
331 {
332     int		pos = 0;
333     int 	c;
334     bool	shifted = false;
335 
336 #ifdef USE_VIRTUAL_SCREEN
337     // Ensure the latest text is on screen.
338     hamfake_rebuildScreen();
339     if (initialtext)
340 	strcpy(glbInputData.myText, initialtext);
341     else
342 	glbInputData.myText[0] = '\0';
343     glbInputData.myMaxLen = len;
344     glbInputData.myX = lx;
345     glbInputData.myY = ly;
346     hamfake_buttonreq(7, 0);
347 
348     // On return we have our string!
349     int		i;
350     for (i = 0; i < len; i++)
351     {
352 	// Yes, I trust no one, not even myself.
353 	text[i] = glbInputData.myText[i];
354 	if (!text[i])
355 	    break;
356     }
357     text[i] = '\0';
358     return;
359 #else
360 
361     // Wait for the person to release the a button...
362 #ifndef HAS_KEYBOARD
363     while (ctrl_anyrawpressed())
364     {
365 	hamfake_awaitEvent();
366     }
367 #endif
368 
369     // Clear any keyboard buffer.
370     hamfake_clearKeyboardBuffer();
371 
372     // Input the initial text.
373     if (initialtext)
374     {
375 	while (*initialtext)
376 	{
377 	    c = *initialtext++;
378 	    if (pos < len)
379 	    {
380 		text[pos] = c;
381 		gfx_printchar(lx+pos, ly, c);
382 		pos++;
383 	    }
384 	}
385     }
386 
387     gfx_nudgecenter(-TILEWIDTH/2, TILEHEIGHT/2);
388     input_drawkeyboard(shifted);
389 
390 #ifdef USE_VIRTUAL_SCREEN
391     hamfake_hasbeenshaken();
392 #endif
393 
394     while (1)
395     {
396 	gfx_printchar(lx+pos, ly, SYMBOL_CURSOR);
397 	c = input_getchar(gx, gy, shifted);
398 
399 	if (hamfake_forceQuit())
400 	{
401 	    c = '\n';
402 	}
403 
404 	switch (c)
405 	{
406 	    case '\r':
407 	    case '\n':
408 		text[pos] = '\0';
409 		// Our absolute tiles need to be reset.
410 		gfx_nudgecenter(0, 0);
411 		gfx_refreshtiles();
412 		return;
413 
414 	    case '\b':
415 		if (pos)
416 		{
417 		    gfx_printchar(lx+pos, ly, ' ');
418 		    pos--;
419 		}
420 		break;
421 
422 	    case '\t':
423 		shifted = !shifted;
424 		input_drawkeyboard(shifted);
425 		break;
426 
427 	    case GFX_KEYF3:	// F3 because it is a tridude.
428 	    case SYMBOL_TRIDUDE:
429 		while (pos)
430 		{
431 		    gfx_printchar(lx+pos, ly, ' ');
432 		    pos--;
433 		}
434 		rand_name(text, len);
435 		while (text[pos])
436 		{
437 		    gfx_printchar(lx+pos, ly, text[pos]);
438 		    pos++;
439 		}
440 		break;
441 
442 	    default:
443 		if (pos < len && c < 256)
444 		{
445 		    text[pos] = c;
446 		    gfx_printchar(lx+pos, ly, c);
447 		    pos++;
448 		}
449 		break;
450 	}
451 	hamfake_rebuildScreen();
452     }
453 
454     // Our absolute tiles need to be reset.
455     gfx_nudgecenter(0, 0);
456     gfx_refreshtiles();
457 #endif
458 }
459 
460