1/*
2 * Copyright (c) 2010-2014 OTClient <https://github.com/edubart/otclient>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include <framework/core/eventdispatcher.h>
24#include "androidwindow.h"
25
26AndroidWindow g_androidWindow;
27
28AndroidWindow::AndroidWindow()
29{
30 m_minimumSize = Size(640, 360);
31 m_size = Size(640, 360);
32 m_eglDisplay = EGL_NO_DISPLAY;
33 m_eglContext = EGL_NO_CONTEXT;
34 m_eglSurface = EGL_NO_SURFACE;
35
36 m_keyMap[AKEYCODE_BACK] = Fw::KeyEscape;
37 m_keyMap[AKEYCODE_VOLUME_UP] = Fw::KeyF1;
38 m_keyMap[AKEYCODE_VOLUME_DOWN] = Fw::KeyF2;
39 m_keyMap[AKEYCODE_CAMERA] = Fw::KeyF3;
40
41 m_keyMap[AKEYCODE_ESCAPE] = Fw::KeyEscape;
42 m_keyMap[AKEYCODE_TAB] = Fw::KeyTab;
43 m_keyMap[AKEYCODE_ENTER] = Fw::KeyEnter;
44 m_keyMap[AKEYCODE_DEL] = Fw::KeyBackspace;
45 m_keyMap[AKEYCODE_SPACE] = Fw::KeySpace;
46
47 m_keyMap[AKEYCODE_PAGE_UP] = Fw::KeyPageUp;
48 m_keyMap[AKEYCODE_PAGE_DOWN] = Fw::KeyPageDown;
49 m_keyMap[AKEYCODE_MOVE_HOME] = Fw::KeyHome;
50 m_keyMap[AKEYCODE_MOVE_END] = Fw::KeyEnd;
51 m_keyMap[AKEYCODE_INSERT] = Fw::KeyInsert;
52 m_keyMap[AKEYCODE_FORWARD_DEL] = Fw::KeyDelete;
53
54 m_keyMap[AKEYCODE_DPAD_UP] = Fw::KeyUp;
55 m_keyMap[AKEYCODE_DPAD_DOWN] = Fw::KeyDown;
56 m_keyMap[AKEYCODE_DPAD_LEFT] = Fw::KeyLeft;
57 m_keyMap[AKEYCODE_DPAD_RIGHT] = Fw::KeyRight;
58
59 m_keyMap[AKEYCODE_NUM_LOCK] = Fw::KeyNumLock;
60 m_keyMap[AKEYCODE_SCROLL_LOCK] = Fw::KeyScrollLock;
61 m_keyMap[AKEYCODE_CAPS_LOCK] = Fw::KeyCapsLock;
62
63 m_keyMap[AKEYCODE_CTRL_LEFT] = Fw::KeyCtrl;
64 m_keyMap[AKEYCODE_CTRL_RIGHT] = Fw::KeyCtrl;
65 m_keyMap[AKEYCODE_SHIFT_LEFT] = Fw::KeyShift;
66 m_keyMap[AKEYCODE_SHIFT_RIGHT] = Fw::KeyShift;
67 m_keyMap[AKEYCODE_ALT_LEFT] = Fw::KeyAlt;
68 m_keyMap[AKEYCODE_ALT_RIGHT] = Fw::KeyAlt;
69
70 m_keyMap[AKEYCODE_0] = Fw::Key0;
71 m_keyMap[AKEYCODE_1] = Fw::Key1;
72 m_keyMap[AKEYCODE_2] = Fw::Key2;
73 m_keyMap[AKEYCODE_3] = Fw::Key3;
74 m_keyMap[AKEYCODE_4] = Fw::Key4;
75 m_keyMap[AKEYCODE_5] = Fw::Key5;
76 m_keyMap[AKEYCODE_6] = Fw::Key6;
77 m_keyMap[AKEYCODE_7] = Fw::Key7;
78 m_keyMap[AKEYCODE_8] = Fw::Key8;
79 m_keyMap[AKEYCODE_9] = Fw::Key9;
80
81 m_keyMap[AKEYCODE_A] = Fw::KeyA;
82 m_keyMap[AKEYCODE_B] = Fw::KeyB;
83 m_keyMap[AKEYCODE_C] = Fw::KeyC;
84 m_keyMap[AKEYCODE_D] = Fw::KeyD;
85 m_keyMap[AKEYCODE_E] = Fw::KeyE;
86 m_keyMap[AKEYCODE_F] = Fw::KeyF;
87 m_keyMap[AKEYCODE_G] = Fw::KeyG;
88 m_keyMap[AKEYCODE_H] = Fw::KeyH;
89 m_keyMap[AKEYCODE_I] = Fw::KeyI;
90 m_keyMap[AKEYCODE_J] = Fw::KeyJ;
91 m_keyMap[AKEYCODE_K] = Fw::KeyK;
92 m_keyMap[AKEYCODE_L] = Fw::KeyL;
93 m_keyMap[AKEYCODE_M] = Fw::KeyM;
94 m_keyMap[AKEYCODE_N] = Fw::KeyN;
95 m_keyMap[AKEYCODE_O] = Fw::KeyO;
96 m_keyMap[AKEYCODE_P] = Fw::KeyP;
97 m_keyMap[AKEYCODE_Q] = Fw::KeyQ;
98 m_keyMap[AKEYCODE_R] = Fw::KeyR;
99 m_keyMap[AKEYCODE_S] = Fw::KeyS;
100 m_keyMap[AKEYCODE_T] = Fw::KeyT;
101 m_keyMap[AKEYCODE_U] = Fw::KeyU;
102 m_keyMap[AKEYCODE_V] = Fw::KeyV;
103 m_keyMap[AKEYCODE_W] = Fw::KeyW;
104 m_keyMap[AKEYCODE_X] = Fw::KeyX;
105 m_keyMap[AKEYCODE_Y] = Fw::KeyY;
106 m_keyMap[AKEYCODE_Z] = Fw::KeyZ;
107
108 m_keyMap[AKEYCODE_SEMICOLON] = Fw::KeySemicolon;
109 m_keyMap[AKEYCODE_SLASH] = Fw::KeySlash;
110 m_keyMap[AKEYCODE_GRAVE] = Fw::KeyGrave;
111 m_keyMap[AKEYCODE_LEFT_BRACKET] = Fw::KeyLeftBracket;
112 m_keyMap[AKEYCODE_BACKSLASH] = Fw::KeyBackslash;
113 m_keyMap[AKEYCODE_RIGHT_BRACKET] = Fw::KeyRightBracket;
114 m_keyMap[AKEYCODE_APOSTROPHE] = Fw::KeyApostrophe;
115 m_keyMap[AKEYCODE_MINUS] = Fw::KeyMinus;
116 m_keyMap[AKEYCODE_EQUALS] = Fw::KeyEqual;
117 m_keyMap[AKEYCODE_COMMA] = Fw::KeyComma;
118 m_keyMap[AKEYCODE_PERIOD] = Fw::KeyPeriod;
119
120 m_keyMap[AKEYCODE_F1] = Fw::KeyF1;
121 m_keyMap[AKEYCODE_F2] = Fw::KeyF2;
122 m_keyMap[AKEYCODE_F3] = Fw::KeyF3;
123 m_keyMap[AKEYCODE_F4] = Fw::KeyF4;
124 m_keyMap[AKEYCODE_F5] = Fw::KeyF5;
125 m_keyMap[AKEYCODE_F6] = Fw::KeyF6;
126 m_keyMap[AKEYCODE_F7] = Fw::KeyF7;
127 m_keyMap[AKEYCODE_F8] = Fw::KeyF8;
128 m_keyMap[AKEYCODE_F9] = Fw::KeyF9;
129 m_keyMap[AKEYCODE_F10] = Fw::KeyF10;
130 m_keyMap[AKEYCODE_F11] = Fw::KeyF11;
131 m_keyMap[AKEYCODE_F12] = Fw::KeyF12;
132}
133
134AndroidWindow::~AndroidWindow()
135{
136 internalDestroyGL();
137}
138
139
140void AndroidWindow::internalInitGL()
141{
142 const EGLint attribs[] = {
143 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
144 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
145 EGL_BLUE_SIZE, 8,
146 EGL_GREEN_SIZE, 8,
147 EGL_RED_SIZE, 8,
148 EGL_ALPHA_SIZE, 8,
149 EGL_NONE
150 };
151 const EGLint context_attrib_list[] = {
152 // request a context using Open GL ES 2.0
153 EGL_CONTEXT_CLIENT_VERSION, 2,
154 EGL_NONE
155 };
156 EGLint format;
157 EGLint numConfigs;
158
159 bool initDisplay = m_eglDisplay == EGL_NO_DISPLAY;
160 if (initDisplay)
161 m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
162 if (m_eglDisplay == EGL_NO_DISPLAY)
163 g_logger.fatal("EGL not supported");
164
165 if (initDisplay) {
166 if (!eglInitialize(m_eglDisplay, 0, 0))
167 g_logger.fatal("Unable to initialize EGL");
168
169 eglChooseConfig(m_eglDisplay, attribs, &m_eglConfig, 1, &numConfigs);
170 eglGetConfigAttrib(m_eglDisplay, m_eglConfig, EGL_NATIVE_VISUAL_ID, &format);
171 ANativeWindow_setBuffersGeometry(g_androidState->window, 0, 0, format);
172 }
173
174 m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, g_androidState->window, NULL);
175 if (m_eglContext == EGL_NO_CONTEXT)
176 m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, NULL, context_attrib_list);
177 if (m_eglContext == EGL_NO_CONTEXT)
178 g_logger.fatal(stdext::format("Unable to create EGL context: %i", eglGetError()));
179 if (eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext) == EGL_FALSE)
180 g_logger.fatal(stdext::format("Unable to eglMakeCurrent: %i", eglGetError()));
181
182 updateSize();
183}
184
185void AndroidWindow::internalDestroyGL()
186{
187 if (m_eglDisplay != EGL_NO_DISPLAY) {
188 eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
189 //if (m_eglContext != EGL_NO_CONTEXT) {
190 // eglDestroyContext(m_eglDisplay, m_eglContext);
191 //}
192 if (m_eglSurface != EGL_NO_SURFACE) {
193 eglDestroySurface(m_eglDisplay, m_eglSurface);
194 }
195 //eglTerminate(m_eglDisplay);
196 }
197 //m_eglDisplay = EGL_NO_DISPLAY;
198 //m_eglContext = EGL_NO_CONTEXT;
199 m_eglSurface = EGL_NO_SURFACE;
200}
201
202void AndroidWindow::internalCheckGL()
203{
204 m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
205 if (m_eglDisplay == EGL_NO_DISPLAY)
206 g_logger.fatal("EGL not supported");
207
208 if (!eglInitialize(m_eglDisplay, NULL, NULL))
209 g_logger.fatal("Unable to initialize EGL");
210}
211
212void AndroidWindow::internalChooseGL()
213{
214
215}
216
217void AndroidWindow::internalCreateGLContext()
218{
219 EGLint attrList[] = {
220#if OPENGL_ES==2
221 EGL_CONTEXT_CLIENT_VERSION, 2,
222#else
223 EGL_CONTEXT_CLIENT_VERSION, 1,
224#endif
225 EGL_NONE
226 };
227
228 m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, attrList);
229 if (m_eglContext == EGL_NO_CONTEXT)
230 g_logger.fatal(stdext::format("Unable to create EGL context: %s", eglGetError()));
231
232 if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext))
233 g_logger.fatal("Unable to connect EGL context into Android native window");
234}
235
236void AndroidWindow::internalDestroyGLContext()
237{
238 /*
239 if (m_window == NULL)
240 return;
241
242 if(m_eglDisplay) {
243 eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
244
245 if (m_eglSurface) {
246 eglDestroySurface(m_eglDisplay, m_eglSurface);
247 m_eglSurface = EGL_NO_SURFACE;
248 }
249
250 if(m_eglContext) {
251 eglDestroyContext(m_eglDisplay, m_eglContext);
252 m_eglContext = EGL_NO_CONTEXT;
253 }
254
255 eglTerminate(m_eglDisplay);
256 m_eglDisplay = EGL_NO_DISPLAY;
257 } */
258}
259
260void AndroidWindow::internalConnectGLContext()
261{
262 /*
263 m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, m_window, NULL);
264 if(m_eglSurface == EGL_NO_SURFACE)
265 g_logger.fatal(stdext::format("Unable to create EGL surface: %s", eglGetError())); */
266}
267
268void AndroidWindow::terminate()
269{
270 //nativePause();
271}
272
273void AndroidWindow::poll()
274{
275 int ident;
276 int events;
277 struct android_poll_source* source;
278
279 // If not animating, we will block forever waiting for events.
280 // If animating, we loop until all events are read, then continue
281 // to draw the next frame of animation.
282 while ((ident = ALooper_pollAll(0, NULL, &events, (void**)&source)) >= 0) {
283 // Process this event.
284 if (source != NULL) {
285 source->process(g_androidState, source);
286 }
287 }
288
289 g_dispatcher.addEvent(std::bind(&AndroidWindow::fireKeysPress, this));
290}
291
292void AndroidWindow::swapBuffers()
293{
294 eglSwapBuffers(m_eglDisplay, m_eglSurface);
295}
296
297void AndroidWindow::setVerticalSync(bool enable)
298{
299 //eglSwapInterval(m_eglDisplay, enable ? 1 : 0);
300}
301
302std::string AndroidWindow::getClipboardText()
303{
304 // TODO
305 return "";
306}
307
308void AndroidWindow::setClipboardText(const std::string& text)
309{
310 // TODO
311}
312
313Size AndroidWindow::getDisplaySize()
314{
315 return m_size;
316}
317
318std::string AndroidWindow::getPlatformType()
319{
320 return "ANDROID-EGL";
321}
322
323void AndroidWindow::init() {}
324
325void AndroidWindow::init(struct android_app* app)
326{
327 g_androidState = app;
328 g_androidState->userData = (void*)this;
329 g_androidState->onAppCmd = static_cast<void(*)(android_app*, int32_t)>(+[](android_app* app, int32_t cmd) -> void {
330 AndroidWindow* window = (AndroidWindow*)app->userData;
331 if (window)
332 return window->handleCmd(cmd);
333 });
334 g_androidState->onInputEvent = static_cast<int32_t(*)(android_app*, AInputEvent*)>(+[](android_app* app, AInputEvent* event) -> int32_t {
335 AndroidWindow* window = (AndroidWindow*)app->userData;
336 if (window)
337 return window->handleInput(event);
338 return 0;
339 });
340}
341
342void AndroidWindow::show() {}
343
344void AndroidWindow::hide() {}
345
346void AndroidWindow::minimize() {}
347
348void AndroidWindow::maximize() {}
349
350void AndroidWindow::move(const Point& pos) {}
351
352void AndroidWindow::resize(const Size& size) {}
353
354void AndroidWindow::showMouse() {}
355
356void AndroidWindow::hideMouse() {}
357
358void AndroidWindow::setMouseCursor(int cursorId) {}
359
360void AndroidWindow::restoreMouseCursor() {}
361
362void AndroidWindow::setTitle(const std::string& title) {}
363
364void AndroidWindow::setMinimumSize(const Size& minimumSize) {}
365
366void AndroidWindow::setFullscreen(bool fullscreen) {}
367
368void AndroidWindow::setIcon(const std::string& iconFile) {}
369
370
371void AndroidWindow::handleCmd(int32_t cmd)
372{
373 switch (cmd) {
374 case APP_CMD_SAVE_STATE:
375 break;
376 case APP_CMD_INIT_WINDOW:
377 if (g_androidState->window != NULL) {
378 internalInitGL();
379 }
380 releaseAllKeys();
381 break;
382 case APP_CMD_TERM_WINDOW:
383 m_visible = false;
384 internalDestroyGL();
385 releaseAllKeys();
386 break;
387 case APP_CMD_GAINED_FOCUS:
388 m_visible = (m_eglContext != EGL_NO_CONTEXT);
389 releaseAllKeys();
390 break;
391 case APP_CMD_LOST_FOCUS:
392 //m_visible = false;
393 releaseAllKeys();
394 break;
395 case APP_CMD_PAUSE:
396 case APP_CMD_STOP:
397 m_visible = false;
398 releaseAllKeys();
399 break;
400 case APP_CMD_DESTROY:
401 if (m_onClose)
402 m_onClose();
403 break;
404 }
405 updateSize();
406}
407
408int AndroidWindow::handleInput(AInputEvent* event)
409{
410 int32_t eventType = AInputEvent_getType(event);
411 int32_t action = AKeyEvent_getAction(event);
412 int32_t key_val = eventType == AINPUT_EVENT_TYPE_KEY ? AKeyEvent_getKeyCode(event) : 0;
413 int pointerId = 0;
414 Point mousePos;
415 if (eventType == AINPUT_EVENT_TYPE_MOTION) {
416 pointerId = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
417 mousePos = Point(AMotionEvent_getX(event, pointerId) / m_scaling, AMotionEvent_getY(event, pointerId) / m_scaling);
418 }
419 g_dispatcher.addEventEx("AndroidWindow::handleInput", [&, eventType, action, key_val, pointerId, mousePos] {
420 if (!m_onInputEvent) return;
421 static ticks_t lastPress = 0;
422 static bool pressed = false;
423 static Point touchStartPos(0, 0);
424 if (eventType == AINPUT_EVENT_TYPE_MOTION) {
425 int actionType = action & AMOTION_EVENT_ACTION_MASK;
426 if (pointerId == 1 || pointerId == 2) { // multitouch
427 if (actionType == AMOTION_EVENT_ACTION_POINTER_DOWN) {
428 m_multiInputEvent[pointerId].reset(Fw::MousePressInputEvent);
429 m_multiInputEvent[pointerId].mousePos = mousePos;
430 m_multiInputEvent[pointerId].mouseButton = pointerId == 1 ? Fw::MouseTouch2 : Fw::MouseTouch3;
431 m_mouseButtonStates[m_multiInputEvent[pointerId].mouseButton] = true;
432 m_onInputEvent(m_multiInputEvent[pointerId]);
433 } else if (actionType == AMOTION_EVENT_ACTION_POINTER_UP) {
434 m_multiInputEvent[pointerId].reset(Fw::MouseReleaseInputEvent);
435 m_multiInputEvent[pointerId].mousePos = mousePos;
436 m_multiInputEvent[pointerId].mouseButton = pointerId == 1 ? Fw::MouseTouch2 : Fw::MouseTouch3;
437 m_mouseButtonStates[m_multiInputEvent[pointerId].mouseButton] = false;
438 m_onInputEvent(m_multiInputEvent[pointerId]);
439 }
440 return;
441 }
442
443 m_inputEvent.reset(Fw::MouseMoveInputEvent);
444 m_inputEvent.mouseMoved = mousePos - m_inputEvent.mousePos;
445 m_inputEvent.mousePos = mousePos;
446 m_onInputEvent(m_inputEvent);
447
448 if (actionType == AMOTION_EVENT_ACTION_DOWN) {
449 lastPress = g_clock.millis();
450 m_inputEvent.reset(Fw::MousePressInputEvent);
451 m_inputEvent.mouseButton = Fw::MouseTouch;
452 m_mouseButtonStates[Fw::MouseTouch] = true;
453 m_onInputEvent(m_inputEvent);
454 touchStartPos = mousePos;
455 } else if (actionType == AMOTION_EVENT_ACTION_UP || actionType == AMOTION_EVENT_ACTION_POINTER_UP) {
456 m_inputEvent.reset(Fw::MouseReleaseInputEvent);
457 m_inputEvent.mouseButton = Fw::MouseTouch;
458 m_mouseButtonStates[Fw::MouseTouch] = false;
459 m_onInputEvent(m_inputEvent);
460 if (pointerId == 0) {
461 if (!pressed) {
462 if (lastPress + 500 < stdext::millis()) {
463 m_inputEvent.reset(Fw::MousePressInputEvent);
464 m_inputEvent.mouseButton = Fw::MouseRightButton;
465 m_onInputEvent(m_inputEvent);
466
467 m_inputEvent.reset(Fw::MouseReleaseInputEvent);
468 m_inputEvent.mouseButton = Fw::MouseRightButton;
469 m_onInputEvent(m_inputEvent);
470 } else {
471 lastPress = stdext::millis();
472 m_inputEvent.reset(Fw::MousePressInputEvent);
473 m_inputEvent.mouseButton = Fw::MouseLeftButton;
474 m_onInputEvent(m_inputEvent);
475
476 m_inputEvent.reset(Fw::MouseReleaseInputEvent);
477 m_inputEvent.mouseButton = Fw::MouseLeftButton;
478 m_onInputEvent(m_inputEvent);
479 }
480 } else {
481 pressed = false;
482 m_inputEvent.reset(Fw::MouseReleaseInputEvent);
483 m_inputEvent.mouseButton = Fw::MouseLeftButton;
484 m_mouseButtonStates[Fw::MouseLeftButton] = false;
485 m_onInputEvent(m_inputEvent);
486 }
487 }
488 } else if (actionType == AMOTION_EVENT_ACTION_MOVE) {
489 int dist = std::max(std::abs(touchStartPos.x - mousePos.x), std::abs(touchStartPos.y - mousePos.y));
490 if (!pressed && dist > 8) {
491 pressed = true;
492 m_inputEvent.reset(Fw::MouseMoveInputEvent);
493 m_inputEvent.mouseMoved = touchStartPos - m_inputEvent.mousePos;
494 m_inputEvent.mousePos = touchStartPos;
495 m_onInputEvent(m_inputEvent);
496
497 m_inputEvent.reset(Fw::MousePressInputEvent);
498 m_inputEvent.mouseButton = Fw::MouseLeftButton;
499 m_mouseButtonStates[Fw::MouseLeftButton] = true;
500 m_onInputEvent(m_inputEvent);
501 }
502 }
503 } else if (eventType == AINPUT_EVENT_TYPE_KEY) {
504 Fw::Key key = Fw::KeyUnknown;
505 if (m_keyMap.find(key_val) != m_keyMap.end())
506 key = m_keyMap[key_val];
507 if (action == AKEY_EVENT_ACTION_DOWN) {
508 processKeyDown(key);
509 } else if (action == AKEY_EVENT_ACTION_UP) {
510 processKeyUp(key);
511 }
512 }
513 });
514 if (eventType == AINPUT_EVENT_TYPE_MOTION) {
515 return 1;
516 } else if (eventType == AINPUT_EVENT_TYPE_KEY) {
517 Fw::Key key = Fw::KeyUnknown;
518 if (m_keyMap.find(key_val) != m_keyMap.end())
519 key = m_keyMap[key_val];
520 return key != Fw::KeyUnknown ? 1 : 0;
521 }
522 return 0;
523}
524
525void AndroidWindow::updateSize()
526{
527 if (std::this_thread::get_id() != g_graphicsThreadId) {
528 g_graphicsDispatcher.addEvent(std::bind(&AndroidWindow::updateSize, this));
529 return;
530 }
531 if (m_eglDisplay == EGL_NO_DISPLAY || m_eglSurface == EGL_NO_SURFACE) return;
532 EGLint w, h;
533 eglQuerySurface(m_eglDisplay, m_eglSurface, EGL_WIDTH, &w);
534 eglQuerySurface(m_eglDisplay, m_eglSurface, EGL_HEIGHT, &h);
535 Size new_size(w, h);
536 if (new_size == m_size) return;
537 m_size = new_size;
538 if (m_onResize)
539 m_onResize(new_size);
540}
541
542
543void AndroidWindow::handleTextInput(std::string text)
544{
545 if (!m_onInputEvent) return;
546 g_dispatcher.addEventEx("AndroidWindow::handleTextInput", [&, text] {
547 if (!m_onInputEvent) return;
548 m_inputEvent.reset(Fw::KeyTextInputEvent);
549 m_inputEvent.keyText = text;
550 m_onInputEvent(m_inputEvent);
551 });
552}
553
554void AndroidWindow::showTextEditor(const std::string& title, const std::string& description, const std::string& text, int flags)
555{
556 g_graphicsDispatcher.addEvent([&, title, description, text, flags] {
557 JNIEnv* env = getJNIEnv();
558 JavaVMAttachArgs javaVMAttachArgs;
559 javaVMAttachArgs.version = JNI_VERSION_1_6;
560 javaVMAttachArgs.name = "NativeThread";
561 javaVMAttachArgs.group = NULL;
562
563 jint nResult = g_androidState->activity->vm->AttachCurrentThread(&env, &javaVMAttachArgs);
564 if (nResult != JNI_ERR) {
565 jmethodID MethodShowKeyboard = env->GetMethodID(env->GetObjectClass(g_androidState->activity->clazz), "showTextEdit", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");
566 env->CallVoidMethod(g_androidState->activity->clazz, MethodShowKeyboard, env->NewStringUTF(title.c_str()), env->NewStringUTF(description.c_str()), env->NewStringUTF(text.c_str()), flags);
567 g_androidState->activity->vm->DetachCurrentThread();
568 }
569 });
570}
571
572void AndroidWindow::displayFatalError(const std::string& message)
573{
574 JNIEnv* env = getJNIEnv();
575 JavaVM* vm = getJavaVM();
576 jint nResult = vm->AttachCurrentThread(&env, NULL);
577 if (nResult != JNI_ERR) {
578 jclass clazz = env->GetObjectClass(getClazz());
579 jmethodID methodID = env->GetMethodID(clazz, "displayFatalError", "(Ljava/lang/String;)V");
580 jstring jmessage = env->NewStringUTF(message.c_str());
581 env->CallVoidMethod(getClazz(), methodID, jmessage);
582 env->DeleteLocalRef(jmessage);
583 vm->DetachCurrentThread();
584 }
585}
586
587void AndroidWindow::openUrl(std::string url)
588{
589 JNIEnv* env = getJNIEnv();
590 JavaVM* vm = getJavaVM();
591 jint nResult = vm->AttachCurrentThread(&env, NULL);
592 if (nResult != JNI_ERR) {
593 jclass clazz = env->GetObjectClass(getClazz());
594 jmethodID methodID = env->GetMethodID(clazz, "openUrl", "(Ljava/lang/String;)V");
595 jstring jmessage = env->NewStringUTF(url.c_str());
596 env->CallVoidMethod(getClazz(), methodID, jmessage);
597 env->DeleteLocalRef(jmessage);
598 vm->DetachCurrentThread();
599 }
600}
601
602extern "C"
603{
604 JNIEXPORT void JNICALL Java_com_otclientv8_OTClientV8_commitText(
605 JNIEnv* env, jobject obj, jstring text)
606 {
607 const char* newChar = env->GetStringUTFChars(text, NULL);
608 std::string newText = newChar;
609 env->ReleaseStringUTFChars(text, newChar);
610 g_graphicsDispatcher.addEvent([newText] {
611 g_window.handleTextInput(newText);
612 });
613 }
614}
615
616