15 #include "androidGraphicsWindow.h"
16 #include "androidGraphicsStateGuardian.h"
17 #include "config_androiddisplay.h"
18 #include "androidGraphicsPipe.h"
20 #include "graphicsPipe.h"
21 #include "keyboardButton.h"
22 #include "mouseButton.h"
23 #include "clockObject.h"
24 #include "pStatTimer.h"
25 #include "textEncoder.h"
26 #include "throw_event.h"
27 #include "nativeWindowHandle.h"
29 #include "android_native_app_glue.h"
30 #include <android/window.h>
31 #include <android/log.h>
33 extern struct android_app* panda_android_app;
35 TypeHandle AndroidGraphicsWindow::_type_handle;
42 AndroidGraphicsWindow::
50 GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
53 DCAST_INTO_V(android_pipe, _pipe);
55 _egl_display = android_pipe->_egl_display;
58 _app = panda_android_app;
62 add_input_device(device);
70 AndroidGraphicsWindow::
71 ~AndroidGraphicsWindow() {
86 PStatTimer timer(_make_current_pcollector, current_thread);
88 begin_frame_spam(mode);
94 if (_egl_surface == EGL_NO_SURFACE) {
99 DCAST_INTO_R(androidgsg, _gsg,
false);
101 if (eglGetCurrentDisplay() == _egl_display &&
102 eglGetCurrentSurface(EGL_READ) == _egl_surface &&
103 eglGetCurrentSurface(EGL_DRAW) == _egl_surface &&
104 eglGetCurrentContext() == androidgsg->_context) {
109 if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, androidgsg->_context)) {
110 androiddisplay_cat.error() <<
"Failed to call eglMakeCurrent: "
111 << get_egl_error_string(eglGetError()) <<
"\n";
120 androidgsg->reset_if_new();
122 if (mode == FM_render) {
124 clear_cube_map_selection();
128 return _gsg->begin_frame(current_thread);
140 end_frame_spam(mode);
143 if (mode == FM_render) {
148 _gsg->end_frame(current_thread);
150 if (mode == FM_render) {
152 clear_cube_map_selection();
176 if (_egl_surface != EGL_NO_SURFACE) {
177 eglSwapBuffers(_egl_display, _egl_surface);
200 struct android_poll_source* source;
203 while ((looper_id = ALooper_pollAll(0, NULL, &events, (
void**)&source)) >= 0) {
205 if (source != NULL) {
206 source->process(_app, source);
244 uint32_t add_flags = 0;
245 uint32_t del_flags = 0;
246 if (_properties.get_fullscreen()) {
247 add_flags |= AWINDOW_FLAG_FULLSCREEN;
249 del_flags |= AWINDOW_FLAG_FULLSCREEN;
251 ANativeActivity_setWindowFlags(_app->activity, add_flags, del_flags);
264 void AndroidGraphicsWindow::
272 GraphicsWindow::close_window();
282 bool AndroidGraphicsWindow::
294 DCAST_INTO_R(androidgsg, _gsg,
false);
303 assert(_app != NULL);
304 _app->userData =
this;
305 _app->onAppCmd = handle_command;
306 _app->onInputEvent = handle_input_event;
309 while (_app->window == NULL) {
314 if (_egl_surface == EGL_NO_SURFACE) {
319 _properties.set_origin(0, 0);
320 _properties.set_cursor_hidden(
true);
321 _properties.set_undecorated(
true);
324 (_fb_properties, androidgsg->get_gl_renderer())) {
331 androiddisplay_cat.error() <<
"open_window done\n";
341 void AndroidGraphicsWindow::
343 if (_egl_surface != EGL_NO_SURFACE) {
344 if (!eglDestroySurface(_egl_display, _egl_surface)) {
345 androiddisplay_cat.error() <<
"Failed to destroy surface: "
346 << get_egl_error_string(eglGetError()) <<
"\n";
348 _egl_surface = EGL_NO_SURFACE;
354 DCAST_INTO_V(androidgsg, _gsg);
364 bool AndroidGraphicsWindow::
367 DCAST_INTO_R(androidgsg, _gsg,
false);
370 ANativeWindow_setBuffersGeometry(_app->window, 0, 0, androidgsg->_format);
373 uint32_t add_flags = 0;
374 uint32_t del_flags = 0;
375 if (_properties.get_fullscreen()) {
376 add_flags |= AWINDOW_FLAG_FULLSCREEN;
378 del_flags |= AWINDOW_FLAG_FULLSCREEN;
380 ANativeActivity_setWindowFlags(_app->activity, add_flags, del_flags);
383 _egl_surface = eglCreateWindowSurface(_egl_display, androidgsg->_fbconfig, _app->window, NULL);
384 if (eglGetError() != EGL_SUCCESS) {
385 androiddisplay_cat.error()
386 <<
"Failed to create window surface.\n";
391 if (androidgsg->_context == EGL_NO_CONTEXT) {
392 androiddisplay_cat.error() <<
"creating context\n";
399 if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, androidgsg->_context)) {
400 androiddisplay_cat.error() <<
"Failed to call eglMakeCurrent: "
401 << get_egl_error_string(eglGetError()) <<
"\n";
409 androidgsg->reset_if_new();
410 if (!androidgsg->is_valid()) {
423 void AndroidGraphicsWindow::
424 handle_command(
struct android_app *app, int32_t command) {
426 window->ns_handle_command(command);
434 void AndroidGraphicsWindow::
435 ns_handle_command(int32_t command) {
439 case APP_CMD_SAVE_STATE:
445 case APP_CMD_INIT_WINDOW:
447 if (_app->window != NULL) {
449 properties.
set_size(ANativeWindow_getWidth(_app->window),
450 ANativeWindow_getHeight(_app->window));
452 system_changed_properties(properties);
455 case APP_CMD_CONFIG_CHANGED:
456 properties.
set_size(ANativeWindow_getWidth(_app->window),
457 ANativeWindow_getHeight(_app->window));
458 system_changed_properties(properties);
460 case APP_CMD_TERM_WINDOW:
463 system_changed_properties(properties);
465 case APP_CMD_WINDOW_RESIZED:
466 properties.
set_size(ANativeWindow_getWidth(_app->window),
467 ANativeWindow_getHeight(_app->window));
469 case APP_CMD_WINDOW_REDRAW_NEEDED:
471 case APP_CMD_CONTENT_RECT_CHANGED:
472 properties.
set_origin(_app->contentRect.left, _app->contentRect.top);
473 properties.
set_size(_app->contentRect.right - _app->contentRect.left,
474 _app->contentRect.bottom - _app->contentRect.top);
475 system_changed_properties(properties);
477 case APP_CMD_GAINED_FOCUS:
479 system_changed_properties(properties);
481 case APP_CMD_LOST_FOCUS:
483 system_changed_properties(properties);
485 case APP_CMD_DESTROY:
488 system_changed_properties(properties);
499 int32_t AndroidGraphicsWindow::
500 handle_input_event(
struct android_app* app, AInputEvent *event) {
503 int32_t event_type = AInputEvent_getType(event);
504 switch (event_type) {
505 case AINPUT_EVENT_TYPE_KEY:
506 return window->handle_key_event(event);
507 case AINPUT_EVENT_TYPE_MOTION:
508 return window->handle_motion_event(event);
518 int32_t AndroidGraphicsWindow::
519 handle_key_event(
const AInputEvent *event) {
541 int32_t keycode = AKeyEvent_getKeyCode(event);
545 androiddisplay_cat.warning()
546 <<
"Unknown keycode: " << keycode <<
"\n";
551 int32_t action = AKeyEvent_getAction(event);
552 if (action == AKEY_EVENT_ACTION_DOWN) {
553 _input_devices[0].button_down(button);
554 }
else if (action == AKEY_EVENT_ACTION_UP) {
555 _input_devices[0].button_up(button);
567 int32_t AndroidGraphicsWindow::
568 handle_motion_event(
const AInputEvent *event) {
569 int32_t action = AMotionEvent_getAction(event);
570 action &= AMOTION_EVENT_ACTION_MASK;
572 if (action == AMOTION_EVENT_ACTION_DOWN) {
574 }
else if (action == AMOTION_EVENT_ACTION_UP) {
578 float x = AMotionEvent_getX(event, 0) - _app->contentRect.left;
579 float y = AMotionEvent_getY(event, 0) - _app->contentRect.top;
581 _input_devices[0].set_pointer_in_window(x, y);
594 map_button(int32_t keycode) {
596 case AKEYCODE_SOFT_LEFT:
597 case AKEYCODE_SOFT_RIGHT:
601 case AKEYCODE_ENDCALL:
627 case AKEYCODE_DPAD_UP:
628 return KeyboardButton::up();
629 case AKEYCODE_DPAD_DOWN:
630 return KeyboardButton::down();
631 case AKEYCODE_DPAD_LEFT:
632 return KeyboardButton::left();
633 case AKEYCODE_DPAD_RIGHT:
634 return KeyboardButton::right();
635 case AKEYCODE_DPAD_CENTER:
636 case AKEYCODE_VOLUME_UP:
637 case AKEYCODE_VOLUME_DOWN:
639 case AKEYCODE_CAMERA:
696 case AKEYCODE_PERIOD:
698 case AKEYCODE_ALT_LEFT:
699 return KeyboardButton::lalt();
700 case AKEYCODE_ALT_RIGHT:
701 return KeyboardButton::ralt();
702 case AKEYCODE_SHIFT_LEFT:
703 return KeyboardButton::lshift();
704 case AKEYCODE_SHIFT_RIGHT:
705 return KeyboardButton::rshift();
707 return KeyboardButton::tab();
709 return KeyboardButton::space();
711 case AKEYCODE_EXPLORER:
712 case AKEYCODE_ENVELOPE:
715 return KeyboardButton::enter();
717 return KeyboardButton::del();
722 case AKEYCODE_EQUALS:
724 case AKEYCODE_LEFT_BRACKET:
726 case AKEYCODE_RIGHT_BRACKET:
728 case AKEYCODE_BACKSLASH:
730 case AKEYCODE_SEMICOLON:
732 case AKEYCODE_APOSTROPHE:
739 case AKEYCODE_HEADSETHOOK:
745 case AKEYCODE_NOTIFICATION:
746 case AKEYCODE_SEARCH:
747 case AKEYCODE_MEDIA_PLAY_PAUSE:
748 case AKEYCODE_MEDIA_STOP:
749 case AKEYCODE_MEDIA_NEXT:
750 case AKEYCODE_MEDIA_PREVIOUS:
751 case AKEYCODE_MEDIA_REWIND:
752 case AKEYCODE_MEDIA_FAST_FORWARD:
755 case AKEYCODE_PAGE_UP:
756 return KeyboardButton::page_up();
757 case AKEYCODE_PAGE_DOWN:
758 return KeyboardButton::page_down();
759 case AKEYCODE_PICTSYMBOLS:
760 case AKEYCODE_SWITCH_CHARSET:
761 case AKEYCODE_BUTTON_A:
762 case AKEYCODE_BUTTON_B:
763 case AKEYCODE_BUTTON_C:
764 case AKEYCODE_BUTTON_X:
765 case AKEYCODE_BUTTON_Y:
766 case AKEYCODE_BUTTON_Z:
767 case AKEYCODE_BUTTON_L1:
768 case AKEYCODE_BUTTON_R1:
769 case AKEYCODE_BUTTON_L2:
770 case AKEYCODE_BUTTON_R2:
771 case AKEYCODE_BUTTON_THUMBL:
772 case AKEYCODE_BUTTON_THUMBR:
773 case AKEYCODE_BUTTON_START:
774 case AKEYCODE_BUTTON_SELECT:
775 case AKEYCODE_BUTTON_MODE:
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
virtual void end_flip()
This function will be called within the draw thread after begin_flip() has been called on all windows...
virtual void end_flip()
This function will be called within the draw thread after begin_flip() has been called on all windows...
This graphics pipe represents the interface for creating OpenGL ES graphics windows on an X-based (e...
virtual void end_frame(FrameMode mode, Thread *current_thread)
This function will be called within the draw thread after rendering is completed for a given frame...
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
bool create_context()
Creates the context based on the config previously obtained in choose_pixel_format.
void set_size(const LVector2i &size)
Specifies the requested size of the window, in pixels.
void choose_pixel_format(const FrameBufferProperties &properties, bool need_pbuffer, bool need_pixmap)
Selects a visual or fbconfig for all the windows and buffers that use this gsg.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
bool subsumes(const FrameBufferProperties &other) const
Returns true if this set of properties makes strictly greater or equal demands of the framebuffer tha...
bool get_fullscreen() const
Returns true if the window is in fullscreen mode.
An interface to manage Android windows and their appropriate EGL surfaces.
bool verify_hardware_software(const FrameBufferProperties &props, const string &renderer) const
Validates that the properties represent the desired kind of renderer (hardware or software)...
A window, fullscreen or on a desktop, into which a graphics device sends its output for interactive d...
A container for the various kinds of properties we might ask to have on a graphics window before we o...
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
An object to create GraphicsOutputs that share a particular 3-D API.
const FrameBufferProperties & get_fb_properties() const
Gets the FrameBufferProperties for all windows and buffers that use this GSG.
void set_origin(const LPoint2i &origin)
Specifies the origin on the screen (in pixels, relative to the top-left corner) at which the window s...
This is a base class for the various different classes that represent the result of a frame of render...
bool is_any_specified() const
Returns true if any properties have been specified, false otherwise.
virtual bool begin_frame(FrameMode mode, Thread *current_thread)
This function will be called within the draw thread before beginning rendering for a given frame...
void set_foreground(bool foreground)
Specifies whether the window should be opened in the foreground (true), or left in the background (fa...
A thread; that is, a lightweight process.
A tiny specialization on GLESGraphicsStateGuardian to add some egl-specific information.
void set_minimized(bool minimized)
Specifies whether the window should be created minimized (true), or normal (false).
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
Encapsulates all the communication with a particular instance of a given rendering backend...
void destroy_context()
Destroys the context previously created by create_context.
This class is the main interface to controlling the render process.
const FrameBufferProperties & get_fb_properties() const
Returns the framebuffer properties of the window.
TypeHandle is the identifier used to differentiate C++ class types.
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
void set_open(bool open)
Specifies whether the window should be open.
void clear_fullscreen()
Removes the fullscreen specification from the properties.
bool has_fullscreen() const
Returns true if set_fullscreen() has been specified.