1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2009 Sam Lantinga
4 
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2.1 of the License, or (at your option) any later version.
9 
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Lesser General Public License for more details.
14 
15     You should have received a copy of the GNU Lesser General Public
16     License along with this library; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 
19     Sam Lantinga
20     slouken@libsdl.org
21 */
22 #include <jni.h>
23 #include <android/log.h>
24 #include <sys/time.h>
25 #include <time.h>
26 #include <stdint.h>
27 #include <math.h>
28 #include <string.h> // for memset()
29 #include <GLES/gl.h>
30 #include <GLES/glext.h>
31 #include <netinet/in.h>
32 
33 #include "SDL_config.h"
34 
35 #include "SDL_version.h"
36 
37 //#include "SDL_opengles.h"
38 #include "SDL_screenkeyboard.h"
39 #include "../SDL_sysvideo.h"
40 #include "SDL_androidvideo.h"
41 #include "SDL_androidinput.h"
42 #include "jniwrapperstuff.h"
43 
44 // #include "touchscreentheme.h" // Not used yet
45 
46 #define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
47 #define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
48 
49 enum { MAX_BUTTONS = SDL_ANDROID_SCREENKEYBOARD_BUTTON_NUM-1, MAX_BUTTONS_AUTOFIRE = 2, BUTTON_TEXT_INPUT = SDL_ANDROID_SCREENKEYBOARD_BUTTON_TEXT-1 } ; // Max amount of custom buttons
50 
51 int SDL_ANDROID_isTouchscreenKeyboardUsed = 0;
52 static int touchscreenKeyboardTheme = 0;
53 static int touchscreenKeyboardShown = 1;
54 static int AutoFireButtonsNum = 0;
55 static int buttonsize = 1;
56 static int transparency = 128;
57 
58 static SDL_Rect arrows, buttons[MAX_BUTTONS], buttonsAutoFireRect[MAX_BUTTONS_AUTOFIRE];
59 static SDLKey buttonKeysyms[MAX_BUTTONS] = {
60 SDL_KEY(SDL_KEY_VAL(SDL_ANDROID_SCREENKB_KEYCODE_0)),
61 SDL_KEY(SDL_KEY_VAL(SDL_ANDROID_SCREENKB_KEYCODE_1)),
62 SDL_KEY(SDL_KEY_VAL(SDL_ANDROID_SCREENKB_KEYCODE_2)),
63 SDL_KEY(SDL_KEY_VAL(SDL_ANDROID_SCREENKB_KEYCODE_3)),
64 SDL_KEY(SDL_KEY_VAL(SDL_ANDROID_SCREENKB_KEYCODE_4)),
65 SDL_KEY(SDL_KEY_VAL(SDL_ANDROID_SCREENKB_KEYCODE_5)),
66 0
67 };
68 
69 enum { ARROW_LEFT = 1, ARROW_RIGHT = 2, ARROW_UP = 4, ARROW_DOWN = 8 };
70 static int oldArrows = 0;
71 static int ButtonAutoFire[MAX_BUTTONS_AUTOFIRE] = {0, 0};
72 static int ButtonAutoFireX[MAX_BUTTONS_AUTOFIRE*2] = {0, 0, 0, 0};
73 static int ButtonAutoFireRot[MAX_BUTTONS_AUTOFIRE] = {0, 0};
74 static int ButtonAutoFireDecay[MAX_BUTTONS_AUTOFIRE] = {0, 0};
75 
76 static int pointerInButtonRect[MAX_BUTTONS + 1] = {0};
77 
78 typedef struct
79 {
80     GLuint id;
81     GLfloat w;
82     GLfloat h;
83 } GLTexture_t;
84 
85 static GLTexture_t arrowImages[5] = { {0, 0, 0}, };
86 static GLTexture_t buttonAutoFireImages[MAX_BUTTONS_AUTOFIRE*2] = { {0, 0, 0}, };
87 static GLTexture_t buttonImages[MAX_BUTTONS*2] = { {0, 0, 0}, };
88 
89 
InsideRect(const SDL_Rect * r,int x,int y)90 static inline int InsideRect(const SDL_Rect * r, int x, int y)
91 {
92 	return ( x >= r->x && x <= r->x + r->w ) && ( y >= r->y && y <= r->y + r->h );
93 }
94 
95 static struct ScreenKbGlState_t
96 {
97 	GLboolean texture2d;
98 	GLuint textureId;
99 	GLfloat color[4];
100 	GLfloat texEnvMode;
101 	GLboolean blend;
102 	GLenum blend1, blend2;
103 	GLint texFilter1, texFilter2;
104 }
105 oldGlState;
106 
beginDrawingTex()107 static inline void beginDrawingTex()
108 {
109 	// Save OpenGL state
110 	// TODO: this code does not work on 1.6 emulator, and on some devices
111 	/*
112 	oldGlState.texture2d = glIsEnabled(GL_TEXTURE_2D);
113 	glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldGlState.textureId);
114 	glGetFloatv(GL_CURRENT_COLOR, &(oldGlState.color[0]));
115 	glGetTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &oldGlState.texEnvMode);
116 	oldGlState.blend = glIsEnabled(GL_BLEND);
117 	glGetIntegerv(GL_BLEND_SRC, &oldGlState.blend1);
118 	glGetIntegerv(GL_BLEND_DST, &oldGlState.blend2);
119 	glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &oldGlState.texFilter1);
120 	glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &oldGlState.texFilter2);
121 	// It's very unlikely that some app will use GL_TEXTURE_CROP_RECT_OES, so just skip it
122 	*/
123 
124 	glEnable(GL_TEXTURE_2D);
125 }
126 
endDrawingTex()127 static inline void endDrawingTex()
128 {
129 	/*
130 	// Restore OpenGL state
131 	if( oldGlState.texture2d == GL_FALSE)
132 		glDisable(GL_TEXTURE_2D);
133 	glBindTexture(GL_TEXTURE_2D, oldGlState.textureId);
134 	glColor4f(oldGlState.color[0], oldGlState.color[1], oldGlState.color[2], oldGlState.color[3]);
135 	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, oldGlState.texEnvMode);
136 	if( oldGlState.blend == GL_FALSE)
137 		glDisable(GL_BLEND);
138 	glBlendFunc(oldGlState.blend1, oldGlState.blend2);
139 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, oldGlState.texFilter1);
140 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, oldGlState.texFilter2);
141 	*/
142 }
143 
drawCharTex(GLTexture_t * tex,SDL_Rect * src,SDL_Rect * dest,Uint8 r,Uint8 g,Uint8 b,Uint8 a)144 static inline void drawCharTex(GLTexture_t * tex, SDL_Rect * src, SDL_Rect * dest, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
145 {
146 	GLint cropRect[4];
147 
148 	if( !dest->h || !dest->w )
149 		return;
150 
151 	glBindTexture(GL_TEXTURE_2D, tex->id);
152 
153 	glColor4x(r * 0x100, g * 0x100, b * 0x100,  a * 0x100 );
154 
155 	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
156 	glEnable(GL_BLEND);
157 	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
158 
159 	if( SDL_ANDROID_SmoothVideo )
160 	{
161 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
162 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
163 	}
164 	else
165 	{
166 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
167 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
168 	}
169 
170 	cropRect[0] = 0;
171 	cropRect[1] = tex->h;
172 	cropRect[2] = tex->w;
173 	cropRect[3] = -tex->h;
174 	if(src)
175 	{
176 		cropRect[0] = src->x;
177 		cropRect[1] = src->h;
178 		cropRect[2] = src->w;
179 		cropRect[3] = -src->h;
180 	}
181 	glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, cropRect);
182 	glDrawTexiOES(dest->x, SDL_ANDROID_sWindowHeight - dest->y - dest->h, 0, dest->w, dest->h);
183 }
184 
SDL_ANDROID_drawTouchscreenKeyboard()185 int SDL_ANDROID_drawTouchscreenKeyboard()
186 {
187 	int i;
188 	int blendFactor;
189 
190 	if( !SDL_ANDROID_isTouchscreenKeyboardUsed || !touchscreenKeyboardShown )
191 		return 0;
192 
193 	blendFactor =		( SDL_GetKeyboardState(NULL)[SDL_KEY(LEFT)] ? 1 : 0 ) +
194 						( SDL_GetKeyboardState(NULL)[SDL_KEY(RIGHT)] ? 1 : 0 ) +
195 						( SDL_GetKeyboardState(NULL)[SDL_KEY(UP)] ? 1 : 0 ) +
196 						( SDL_GetKeyboardState(NULL)[SDL_KEY(DOWN)] ? 1 : 0 );
197 
198 	beginDrawingTex();
199 	if( blendFactor == 0 )
200 		drawCharTex( &arrowImages[0], NULL, &arrows, 255, 255, 255, transparency );
201 	else
202 	{
203 		if( SDL_GetKeyboardState(NULL)[SDL_KEY(LEFT)] )
204 			drawCharTex( &arrowImages[1], NULL, &arrows, 255, 255, 255, transparency / blendFactor );
205 		if( SDL_GetKeyboardState(NULL)[SDL_KEY(RIGHT)] )
206 			drawCharTex( &arrowImages[2], NULL, &arrows, 255, 255, 255, transparency / blendFactor );
207 		if( SDL_GetKeyboardState(NULL)[SDL_KEY(UP)] )
208 			drawCharTex( &arrowImages[3], NULL, &arrows, 255, 255, 255, transparency / blendFactor );
209 		if( SDL_GetKeyboardState(NULL)[SDL_KEY(DOWN)] )
210 			drawCharTex( &arrowImages[4], NULL, &arrows, 255, 255, 255, transparency / blendFactor );
211 	}
212 
213 	for( i = 0; i < MAX_BUTTONS; i++ )
214 	{
215 		if( ! buttons[i].h || ! buttons[i].w )
216 			continue;
217 		if( i < AutoFireButtonsNum )
218 		{
219 			if( ButtonAutoFire[i] == 1 && SDL_GetTicks() - ButtonAutoFireDecay[i] > 1000 )
220 			{
221 				ButtonAutoFire[i] = 0;
222 			}
223 			if( ! ButtonAutoFire[i] && SDL_GetTicks() - ButtonAutoFireDecay[i] > 300 )
224 			{
225 				if( ButtonAutoFireX[i*2] > 0 )
226 					ButtonAutoFireX[i*2] --;
227 				if( ButtonAutoFireX[i*2+1] > 0 )
228 					ButtonAutoFireX[i*2+1] --;
229 				ButtonAutoFireDecay[i] = SDL_GetTicks();
230 			}
231 		}
232 
233 		if( i < AutoFireButtonsNum && ! ButtonAutoFire[i] &&
234 			( ButtonAutoFireX[i*2] > 0 || ButtonAutoFireX[i*2+1] > 0 ) )
235 		{
236 			int pos1src = buttonImages[i*2+1].w / 2 - ButtonAutoFireX[i*2];
237 			int pos1dst = buttons[i].w * pos1src / buttonImages[i*2+1].w;
238 			int pos2src = buttonImages[i*2+1].w - ( buttonImages[i*2+1].w / 2 - ButtonAutoFireX[i*2+1] );
239 			int pos2dst = buttons[i].w * pos2src / buttonImages[i*2+1].w;
240 
241 			SDL_Rect autoFireCrop = { 0, 0, pos1src, buttonImages[i*2+1].h };
242 			SDL_Rect autoFireDest = buttons[i];
243 			autoFireDest.w = pos1dst;
244 
245 			drawCharTex( &buttonImages[i*2+1],
246 						&autoFireCrop, &autoFireDest, 255, 255, 255, transparency );
247 
248 			autoFireCrop.x = pos2src;
249 			autoFireCrop.w = buttonImages[i*2+1].w - pos2src;
250 			autoFireDest.x = buttons[i].x + pos2dst;
251 			autoFireDest.w = buttons[i].w - pos2dst;
252 
253 			drawCharTex( &buttonImages[i*2+1],
254 						&autoFireCrop, &autoFireDest, 255, 255, 255, transparency );
255 
256 			autoFireCrop.x = pos1src;
257 			autoFireCrop.w = pos2src - pos1src;
258 			autoFireDest.x = buttons[i].x + pos1dst;
259 			autoFireDest.w = pos2dst - pos1dst;
260 
261 			drawCharTex( &buttonAutoFireImages[i*2+1],
262 						&autoFireCrop, &autoFireDest, 255, 255, 255, transparency );
263 		}
264 		else
265 		{
266 			drawCharTex( ( i < AutoFireButtonsNum && ButtonAutoFire[i] ) ? &buttonAutoFireImages[i*2] :
267 						&buttonImages[ SDL_GetKeyboardState(NULL)[buttonKeysyms[i]] ? (i * 2 + 1) : (i * 2) ],
268 						NULL, &buttons[i], 255, 255, 255, transparency );
269 		}
270 	}
271 	endDrawingTex();
272 
273 	return 1;
274 };
275 
ArrowKeysPressed(int x,int y)276 static inline int ArrowKeysPressed(int x, int y)
277 {
278 	int ret = 0, dx, dy;
279 	dx = x - arrows.x - arrows.w / 2;
280 	dy = y - arrows.y - arrows.h / 2;
281 	// Single arrow key pressed
282 	if( abs(dy / 2) >= abs(dx) )
283 	{
284 		if( dy < 0 )
285 			ret |= ARROW_UP;
286 		else
287 			ret |= ARROW_DOWN;
288 	}
289 	else
290 	if( abs(dx / 2) >= abs(dy) )
291 	{
292 		if( dx > 0 )
293 			ret |= ARROW_RIGHT;
294 		else
295 			ret |= ARROW_LEFT;
296 	}
297 	else // Two arrow keys pressed
298 	{
299 		if( dx > 0 )
300 			ret |= ARROW_RIGHT;
301 		else
302 			ret |= ARROW_LEFT;
303 
304 		if( dy < 0 )
305 			ret |= ARROW_UP;
306 		else
307 			ret |= ARROW_DOWN;
308 	}
309 	return ret;
310 }
311 
SDL_ANDROID_processTouchscreenKeyboard(int x,int y,int action,int pointerId)312 int SDL_ANDROID_processTouchscreenKeyboard(int x, int y, int action, int pointerId)
313 {
314 	int i;
315 	int processed = 0;
316 
317 
318 	if( !touchscreenKeyboardShown )
319 		return 0;
320 
321 
322 	if( action == MOUSE_DOWN )
323 	{
324 		//__android_log_print(ANDROID_LOG_INFO, "libSDL", "touch %03dx%03d ptr %d action %d", x, y, pointerId, action);
325 		if( InsideRect( &arrows, x, y ) )
326 		{
327 			processed = 1;
328 			if( pointerInButtonRect[MAX_BUTTONS] == -1 )
329 			{
330 				pointerInButtonRect[MAX_BUTTONS] = pointerId;
331 				if( SDL_ANDROID_isJoystickUsed )
332 				{
333 					SDL_ANDROID_MainThreadPushJoystickAxis(0, 0, (x - arrows.x - arrows.w / 2) * 65534 / arrows.w );
334 					SDL_ANDROID_MainThreadPushJoystickAxis(0, 1, (y - arrows.y - arrows.h / 2) * 65534 / arrows.h );
335 				}
336 				else
337 				{
338 					i = ArrowKeysPressed(x, y);
339 					if( i & ARROW_UP )
340 						SDL_ANDROID_MainThreadPushKeyboardKey( SDL_PRESSED, SDL_KEY(UP) );
341 					if( i & ARROW_DOWN )
342 						SDL_ANDROID_MainThreadPushKeyboardKey( SDL_PRESSED, SDL_KEY(DOWN) );
343 					if( i & ARROW_LEFT )
344 						SDL_ANDROID_MainThreadPushKeyboardKey( SDL_PRESSED, SDL_KEY(LEFT) );
345 					if( i & ARROW_RIGHT )
346 						SDL_ANDROID_MainThreadPushKeyboardKey( SDL_PRESSED, SDL_KEY(RIGHT) );
347 					oldArrows = i;
348 				}
349 			}
350 		}
351 
352 		for( i = 0; i < MAX_BUTTONS; i++ )
353 		{
354 			if( ! buttons[i].h || ! buttons[i].w )
355 				continue;
356 			if( InsideRect( &buttons[i], x, y) )
357 			{
358 				processed = 1;
359 				if( pointerInButtonRect[i] == -1 )
360 				{
361 					pointerInButtonRect[i] = pointerId;
362 					if( i == BUTTON_TEXT_INPUT )
363 						SDL_ANDROID_ToggleScreenKeyboardTextInput(NULL);
364 					else
365 						SDL_ANDROID_MainThreadPushKeyboardKey( SDL_PRESSED, buttonKeysyms[i] );
366 					if( i < AutoFireButtonsNum )
367 					{
368 						ButtonAutoFire[i] = 0;
369 						ButtonAutoFireX[i*2] = 0;
370 						ButtonAutoFireX[i*2+1] = 0;
371 						ButtonAutoFireRot[i] = x;
372 						ButtonAutoFireDecay[i] = SDL_GetTicks();
373 					}
374 				}
375 			}
376 		}
377 	}
378 	else
379 	if( action == MOUSE_UP )
380 	{
381 		//__android_log_print(ANDROID_LOG_INFO, "libSDL", "touch %03dx%03d ptr %d action %d", x, y, pointerId, action);
382 		if( pointerInButtonRect[MAX_BUTTONS] == pointerId )
383 		{
384 			processed = 1;
385 			pointerInButtonRect[MAX_BUTTONS] = -1;
386 			if( SDL_ANDROID_isJoystickUsed )
387 			{
388 				SDL_ANDROID_MainThreadPushJoystickAxis(0, 0, 0 );
389 				SDL_ANDROID_MainThreadPushJoystickAxis(0, 1, 0 );
390 			}
391 			else
392 			{
393 				SDL_ANDROID_MainThreadPushKeyboardKey( SDL_RELEASED, SDL_KEY(UP) );
394 				SDL_ANDROID_MainThreadPushKeyboardKey( SDL_RELEASED, SDL_KEY(DOWN) );
395 				SDL_ANDROID_MainThreadPushKeyboardKey( SDL_RELEASED, SDL_KEY(LEFT) );
396 				SDL_ANDROID_MainThreadPushKeyboardKey( SDL_RELEASED, SDL_KEY(RIGHT) );
397 				oldArrows = 0;
398 			}
399 		}
400 		for( i = 0; i < MAX_BUTTONS; i++ )
401 		{
402 			if( ! buttons[i].h || ! buttons[i].w )
403 				continue;
404 			if( pointerInButtonRect[i] == pointerId )
405 			{
406 				processed = 1;
407 				pointerInButtonRect[i] = -1;
408 				if( i < AutoFireButtonsNum && ButtonAutoFire[i] )
409 				{
410 					ButtonAutoFire[i] = 2;
411 				}
412 				else
413 				{
414 					if( i != BUTTON_TEXT_INPUT )
415 						SDL_ANDROID_MainThreadPushKeyboardKey( SDL_RELEASED, buttonKeysyms[i] );
416 				}
417 				if( i < AutoFireButtonsNum )
418 				{
419 					ButtonAutoFireX[i*2] = 0;
420 					ButtonAutoFireX[i*2+1] = 0;
421 				}
422 			}
423 		}
424 	}
425 	else
426 	if( action == MOUSE_MOVE )
427 	{
428 		// Process cases when pointer enters button area (it won't send keypress twice if button already pressed)
429 		processed = SDL_ANDROID_processTouchscreenKeyboard(x, y, MOUSE_DOWN, pointerId);
430 
431 		// Process cases when pointer leaves button area
432 		// TODO: huge code size, split it or somehow make it more readable
433 		if( pointerInButtonRect[MAX_BUTTONS] == pointerId )
434 		{
435 			processed = 1;
436 			if( ! InsideRect( &arrows, x, y ) )
437 			{
438 				pointerInButtonRect[MAX_BUTTONS] = -1;
439 				if( SDL_ANDROID_isJoystickUsed )
440 				{
441 					SDL_ANDROID_MainThreadPushJoystickAxis(0, 0, 0 );
442 					SDL_ANDROID_MainThreadPushJoystickAxis(0, 1, 0 );
443 				}
444 				else
445 				{
446 					SDL_ANDROID_MainThreadPushKeyboardKey( SDL_RELEASED, SDL_KEY(UP) );
447 					SDL_ANDROID_MainThreadPushKeyboardKey( SDL_RELEASED, SDL_KEY(DOWN) );
448 					SDL_ANDROID_MainThreadPushKeyboardKey( SDL_RELEASED, SDL_KEY(LEFT) );
449 					SDL_ANDROID_MainThreadPushKeyboardKey( SDL_RELEASED, SDL_KEY(RIGHT) );
450 					oldArrows = 0;
451 				}
452 			}
453 			else
454 			{
455 				if( SDL_ANDROID_isJoystickUsed )
456 				{
457 					SDL_ANDROID_MainThreadPushJoystickAxis(0, 0, (x - arrows.x - arrows.w / 2) * 65534 / arrows.w );
458 					SDL_ANDROID_MainThreadPushJoystickAxis(0, 1, (y - arrows.y - arrows.h / 2) * 65534 / arrows.h );
459 				}
460 				else
461 				{
462 					i = ArrowKeysPressed(x, y);
463 					if( i != oldArrows )
464 					{
465 						if( oldArrows & ARROW_UP && ! (i & ARROW_UP) )
466 							SDL_ANDROID_MainThreadPushKeyboardKey( SDL_RELEASED, SDL_KEY(UP) );
467 						if( oldArrows & ARROW_DOWN && ! (i & ARROW_DOWN) )
468 							SDL_ANDROID_MainThreadPushKeyboardKey( SDL_RELEASED, SDL_KEY(DOWN) );
469 						if( oldArrows & ARROW_LEFT && ! (i & ARROW_LEFT) )
470 							SDL_ANDROID_MainThreadPushKeyboardKey( SDL_RELEASED, SDL_KEY(LEFT) );
471 						if( oldArrows & ARROW_RIGHT && ! (i & ARROW_RIGHT) )
472 							SDL_ANDROID_MainThreadPushKeyboardKey( SDL_RELEASED, SDL_KEY(RIGHT) );
473 						if( i & ARROW_UP )
474 							SDL_ANDROID_MainThreadPushKeyboardKey( SDL_PRESSED, SDL_KEY(UP) );
475 						if( i & ARROW_DOWN )
476 							SDL_ANDROID_MainThreadPushKeyboardKey( SDL_PRESSED, SDL_KEY(DOWN) );
477 						if( i & ARROW_LEFT )
478 							SDL_ANDROID_MainThreadPushKeyboardKey( SDL_PRESSED, SDL_KEY(LEFT) );
479 						if( i & ARROW_RIGHT )
480 							SDL_ANDROID_MainThreadPushKeyboardKey( SDL_PRESSED, SDL_KEY(RIGHT) );
481 					}
482 					oldArrows = i;
483 				}
484 			}
485 		}
486 		for( i = 0; i < AutoFireButtonsNum; i++ )
487 		{
488 			if( pointerInButtonRect[i] == pointerId )
489 			{
490 				processed = 1;
491 				if( ! InsideRect( &buttonsAutoFireRect[i], x, y ) )
492 				{
493 					pointerInButtonRect[i] = -1;
494 					if( !ButtonAutoFire[i] )
495 					{
496 						if( i != BUTTON_TEXT_INPUT )
497 							SDL_ANDROID_MainThreadPushKeyboardKey( SDL_RELEASED, buttonKeysyms[i] );
498 					}
499 					else
500 					{
501 						ButtonAutoFire[i] = 2;
502 					}
503 					ButtonAutoFireX[i*2] = 0;
504 					ButtonAutoFireX[i*2+1] = 0;
505 				}
506 				else
507 				{
508 					int coeff = (buttonAutoFireImages[i*2+1].w > buttons[i].w) ? buttonAutoFireImages[i*2+1].w / buttons[i].w + 1 : 1;
509 					if( ButtonAutoFireRot[i] < x )
510 						ButtonAutoFireX[i*2+1] += (x - ButtonAutoFireRot[i]) * coeff;
511 					if( ButtonAutoFireRot[i] > x )
512 						ButtonAutoFireX[i*2] += (ButtonAutoFireRot[i] - x) * coeff;
513 
514 					ButtonAutoFireRot[i] = x;
515 
516 					if( ButtonAutoFireX[i*2] < 0 )
517 						ButtonAutoFireX[i*2] = 0;
518 					if( ButtonAutoFireX[i*2+1] < 0 )
519 						ButtonAutoFireX[i*2+1] = 0;
520 					if( ButtonAutoFireX[i*2] > buttonAutoFireImages[i*2+1].w / 2 )
521 						ButtonAutoFireX[i*2] = buttonAutoFireImages[i*2+1].w / 2;
522 					if( ButtonAutoFireX[i*2+1] > buttonAutoFireImages[i*2+1].w / 2 )
523 						ButtonAutoFireX[i*2+1] = buttonAutoFireImages[i*2+1].w / 2;
524 
525 					if( ButtonAutoFireX[i*2] == buttonAutoFireImages[i*2+1].w / 2 &&
526 						ButtonAutoFireX[i*2+1] == buttonAutoFireImages[i*2+1].w / 2 )
527 					{
528 						if( ! ButtonAutoFire[i] )
529 							ButtonAutoFireDecay[i] = SDL_GetTicks();
530 						ButtonAutoFire[i] = 1;
531 					}
532 				}
533 			}
534 		}
535 		for( i = AutoFireButtonsNum; i < MAX_BUTTONS; i++ )
536 		{
537 			if( ! buttons[i].h || ! buttons[i].w )
538 				continue;
539 			if( pointerInButtonRect[i] == pointerId )
540 			{
541 				processed = 1;
542 				if( ! InsideRect( &buttons[i], x, y ) )
543 				{
544 					pointerInButtonRect[i] = -1;
545 					if( i != BUTTON_TEXT_INPUT )
546 						SDL_ANDROID_MainThreadPushKeyboardKey( SDL_RELEASED, buttonKeysyms[i] );
547 				}
548 			}
549 		}
550 	}
551 
552 	return processed;
553 };
554 
555 JNIEXPORT void JNICALL
JAVA_EXPORT_NAME(Settings_nativeSetupScreenKeyboard)556 JAVA_EXPORT_NAME(Settings_nativeSetupScreenKeyboard) ( JNIEnv*  env, jobject thiz, jint size, jint theme, jint nbuttonsAutoFire, jint _transparency )
557 {
558 	int i, ii;
559 	int nbuttons1row, nbuttons2row;
560 	int _nbuttons = MAX_BUTTONS;
561 	touchscreenKeyboardTheme = theme;
562 	AutoFireButtonsNum = nbuttonsAutoFire;
563 	if( AutoFireButtonsNum > MAX_BUTTONS_AUTOFIRE )
564 		AutoFireButtonsNum = MAX_BUTTONS_AUTOFIRE;
565 	// TODO: works for horizontal screen orientation only!
566 	buttonsize = size;
567 	switch(_transparency)
568 	{
569 		case 0: transparency = 16; break;
570 		case 1: transparency = 32; break;
571 		case 2: transparency = 64; break;
572 		case 3: transparency = 128; break;
573 		case 4: transparency = 192; break;
574 		default: transparency = 128; break;
575 	}
576 
577 	// Arrows to the lower-left part of screen
578 	arrows.x = SDL_ANDROID_sWindowWidth / 4;
579 	arrows.y = SDL_ANDROID_sWindowHeight - SDL_ANDROID_sWindowWidth / 4;
580 	arrows.w = SDL_ANDROID_sWindowWidth / (size + 2);
581 	arrows.h = arrows.w;
582 	arrows.x -= arrows.w/2;
583 	arrows.y -= arrows.h/2;
584 	// Move arrows from the center of the screen
585 	arrows.x -= size * SDL_ANDROID_sWindowWidth / 32;
586 	arrows.y += size * SDL_ANDROID_sWindowWidth / 32;
587 
588 	// Buttons to the lower-right in 2 rows
589 	for(i = 0; i < 2; i++)
590 	for(ii = 0; ii < 3; ii++)
591 	{
592 		// Custom button ordering
593 		int iii = ii + i*2;
594 		if( ii == 2 )
595 			iii = 4 + i;
596 		buttons[iii].x = SDL_ANDROID_sWindowWidth - SDL_ANDROID_sWindowWidth / 12 - (SDL_ANDROID_sWindowWidth * ii / 6);
597 		buttons[iii].y = SDL_ANDROID_sWindowHeight - SDL_ANDROID_sWindowHeight / 8 - (SDL_ANDROID_sWindowHeight * i / 4);
598 		buttons[iii].w = SDL_ANDROID_sWindowWidth / (size + 2) / 3;
599 		buttons[iii].h = buttons[iii].w;
600 		buttons[iii].x -= buttons[iii].w/2;
601 		buttons[iii].y -= buttons[iii].h/2;
602 	}
603 	buttons[6].x = 0;
604 	buttons[6].y = 0;
605 	buttons[6].w = SDL_ANDROID_sWindowHeight/10;
606 	buttons[6].h = SDL_ANDROID_sWindowHeight/10;
607 
608 	for( i = 0; i < sizeof(pointerInButtonRect)/sizeof(pointerInButtonRect[0]); i++ )
609 	{
610 		pointerInButtonRect[i] = -1;
611 	}
612 	for( i = 0; i < nbuttonsAutoFire; i++ )
613 	{
614 		buttonsAutoFireRect[i].w = buttons[i].w * 2;
615 		buttonsAutoFireRect[i].h = buttons[i].h * 2;
616 		buttonsAutoFireRect[i].x = buttons[i].x - buttons[i].w / 2;
617 		buttonsAutoFireRect[i].y = buttons[i].y - buttons[i].h / 2;
618 	}
619 };
620 
621 
622 JNIEXPORT void JNICALL
JAVA_EXPORT_NAME(Settings_nativeSetTouchscreenKeyboardUsed)623 JAVA_EXPORT_NAME(Settings_nativeSetTouchscreenKeyboardUsed) ( JNIEnv*  env, jobject thiz)
624 {
625 	SDL_ANDROID_isTouchscreenKeyboardUsed = 1;
626 }
627 
628 static int
power_of_2(int input)629 power_of_2(int input)
630 {
631     int value = 1;
632 
633     while (value < input) {
634         value <<= 1;
635     }
636     return value;
637 }
638 
setupScreenKeyboardButton(int buttonID,Uint8 * charBuf)639 static int setupScreenKeyboardButton( int buttonID, Uint8 * charBuf )
640 {
641 	// TODO: softstretch with antialiasing
642 	int w, h, len, format;
643 	GLTexture_t * data = NULL;
644 	int texture_w, texture_h;
645 
646 	if( buttonID < 5 )
647 		data = &(arrowImages[buttonID]);
648 	else
649 	if( buttonID < 9 )
650 		data = &(buttonAutoFireImages[buttonID-5]);
651 	else
652 		data = &(buttonImages[buttonID-9]);
653 
654 	if( buttonID > 22 ) // Error, array too big
655 		return 12; // Return value bigger than zero to iterate it
656 
657 	memcpy(&w, charBuf, sizeof(int));
658 	memcpy(&h, charBuf + sizeof(int), sizeof(int));
659 	memcpy(&format, charBuf + 2*sizeof(int), sizeof(int));
660 	w = ntohl(w);
661 	h = ntohl(h);
662 	format = ntohl(format);
663 
664 	texture_w = power_of_2(w);
665 	texture_h = power_of_2(h);
666 	data->w = w;
667 	data->h = h;
668 
669 	glEnable(GL_TEXTURE_2D);
670 
671 	glGenTextures(1, &data->id);
672 	glBindTexture(GL_TEXTURE_2D, data->id);
673 	//__android_log_print(ANDROID_LOG_INFO, "libSDL", "On-screen keyboard generated OpenGL texture ID %d", data->id);
674 
675 	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture_w, texture_h, 0, GL_RGBA,
676 					format ? GL_UNSIGNED_SHORT_4_4_4_4 : GL_UNSIGNED_SHORT_5_5_5_1, NULL);
677 	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
678 
679 	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA,
680 						format ? GL_UNSIGNED_SHORT_4_4_4_4 : GL_UNSIGNED_SHORT_5_5_5_1,
681 						charBuf + 3*sizeof(int) );
682 
683 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
684 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
685 
686 	glDisable(GL_TEXTURE_2D);
687 
688 	return 3*sizeof(int) + w * h * 2;
689 }
690 
691 JNIEXPORT void JNICALL
JAVA_EXPORT_NAME(Settings_nativeSetupScreenKeyboardButtons)692 JAVA_EXPORT_NAME(Settings_nativeSetupScreenKeyboardButtons) ( JNIEnv*  env, jobject thiz, jbyteArray charBufJava )
693 {
694 	jboolean isCopy = JNI_TRUE;
695 	int len = (*env)->GetArrayLength(env, charBufJava);
696 	Uint8 * charBuf = (Uint8 *) (*env)->GetByteArrayElements(env, charBufJava, &isCopy);
697 	int but, pos;
698 
699 	for( but = 0, pos = 0; pos < len; but ++ )
700 		pos += setupScreenKeyboardButton( but, charBuf + pos );
701 
702 	(*env)->ReleaseByteArrayElements(env, charBufJava, (jbyte *)charBuf, 0);
703 }
704 
705 
SDL_ANDROID_SetScreenKeyboardButtonPos(int buttonId,SDL_Rect * pos)706 int SDL_ANDROID_SetScreenKeyboardButtonPos(int buttonId, SDL_Rect * pos)
707 {
708 	if( buttonId < 0 || buttonId >= SDL_ANDROID_SCREENKEYBOARD_BUTTON_NUM || ! pos )
709 		return 0;
710 
711 	if( buttonId == SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD )
712 	{
713 		arrows = *pos;
714 	}
715 	else
716 	{
717 		int i = buttonId - SDL_ANDROID_SCREENKEYBOARD_BUTTON_0;
718 		buttons[i] = *pos;
719 		if( i < AutoFireButtonsNum )
720 		{
721 			buttonsAutoFireRect[i].w = buttons[i].w * 2;
722 			buttonsAutoFireRect[i].h = buttons[i].h * 2;
723 			buttonsAutoFireRect[i].x = buttons[i].x - buttons[i].w / 2;
724 			buttonsAutoFireRect[i].y = buttons[i].y - buttons[i].h / 2;
725 		}
726 	}
727 	return 1;
728 };
729 
SDL_ANDROID_GetScreenKeyboardButtonPos(int buttonId,SDL_Rect * pos)730 int SDL_ANDROID_GetScreenKeyboardButtonPos(int buttonId, SDL_Rect * pos)
731 {
732 	if( buttonId < 0 || buttonId >= SDL_ANDROID_SCREENKEYBOARD_BUTTON_NUM || ! pos )
733 		return 0;
734 
735 	if( buttonId == SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD )
736 	{
737 		*pos = arrows;
738 	}
739 	else
740 	{
741 		*pos = buttons[buttonId - SDL_ANDROID_SCREENKEYBOARD_BUTTON_0];
742 	}
743 	return 1;
744 };
745 
SDL_ANDROID_SetScreenKeyboardButtonKey(int buttonId,SDLKey key)746 int SDL_ANDROID_SetScreenKeyboardButtonKey(int buttonId, SDLKey key)
747 {
748 	if( buttonId < SDL_ANDROID_SCREENKEYBOARD_BUTTON_0 || buttonId > SDL_ANDROID_SCREENKEYBOARD_BUTTON_5 || ! key )
749 		return 0;
750 	buttonKeysyms[buttonId - SDL_ANDROID_SCREENKEYBOARD_BUTTON_0] = key;
751 	return 1;
752 };
753 
SDL_ANDROID_GetScreenKeyboardButtonKey(int buttonId)754 SDLKey SDL_ANDROID_GetScreenKeyboardButtonKey(int buttonId)
755 {
756 	if( buttonId < SDL_ANDROID_SCREENKEYBOARD_BUTTON_0 || buttonId > SDL_ANDROID_SCREENKEYBOARD_BUTTON_5 )
757 		return SDLK_UNKNOWN;
758 	return buttonKeysyms[buttonId - SDL_ANDROID_SCREENKEYBOARD_BUTTON_0];
759 };
760 
SDL_ANDROID_SetScreenKeyboardAutoFireButtonsAmount(int nbuttons)761 int SDL_ANDROID_SetScreenKeyboardAutoFireButtonsAmount(int nbuttons)
762 {
763 	if( nbuttons < 0 || nbuttons >= MAX_BUTTONS_AUTOFIRE )
764 		return 0;
765 	AutoFireButtonsNum = nbuttons;
766 	return 1;
767 };
768 
SDL_ANDROID_GetScreenKeyboardAutoFireButtonsAmount()769 int SDL_ANDROID_GetScreenKeyboardAutoFireButtonsAmount()
770 {
771 	return AutoFireButtonsNum;
772 };
773 
SDL_ANDROID_SetScreenKeyboardShown(int shown)774 int SDL_ANDROID_SetScreenKeyboardShown(int shown)
775 {
776 	touchscreenKeyboardShown = shown;
777 };
778 
SDL_ANDROID_GetScreenKeyboardShown()779 int SDL_ANDROID_GetScreenKeyboardShown()
780 {
781 	return touchscreenKeyboardShown;
782 };
783 
SDL_ANDROID_GetScreenKeyboardSize()784 int SDL_ANDROID_GetScreenKeyboardSize()
785 {
786 	return buttonsize;
787 };
788 
SDL_ANDROID_ToggleScreenKeyboardTextInput(const char * previousText)789 int SDL_ANDROID_ToggleScreenKeyboardTextInput(const char * previousText)
790 {
791 	static char textIn[255];
792 	if( previousText == NULL )
793 		previousText = "";
794 	strncpy(textIn, previousText, sizeof(textIn));
795 	textIn[sizeof(textIn)-1] = 0;
796 	SDL_ANDROID_CallJavaShowScreenKeyboard(textIn, NULL, 0);
797 	return 1;
798 };
799 
SDL_ANDROID_GetScreenKeyboardTextInput(char * textBuf,int textBufSize)800 int SDLCALL SDL_ANDROID_GetScreenKeyboardTextInput(char * textBuf, int textBufSize)
801 {
802 	SDL_ANDROID_CallJavaShowScreenKeyboard(textBuf, textBuf, textBufSize);
803 	return 1;
804 };
805 
806