28 #include "android_native_app_glue.h" 29 #include <android/window.h> 30 #include <android/log.h> 32 extern IMPORT_CLASS
struct android_app* panda_android_app;
34 TypeHandle AndroidGraphicsWindow::_type_handle;
39 AndroidGraphicsWindow::
41 const std::string &name,
47 GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host),
48 _mouse_button_state(0)
51 DCAST_INTO_V(android_pipe, _pipe);
53 _egl_display = android_pipe->_egl_display;
56 _app = panda_android_app;
59 add_input_device(device);
66 AndroidGraphicsWindow::
67 ~AndroidGraphicsWindow() {
79 PStatTimer timer(_make_current_pcollector, current_thread);
81 begin_frame_spam(mode);
82 if (_gsg ==
nullptr) {
87 if (_egl_surface == EGL_NO_SURFACE) {
92 DCAST_INTO_R(androidgsg, _gsg,
false);
94 if (eglGetCurrentDisplay() == _egl_display &&
95 eglGetCurrentSurface(EGL_READ) == _egl_surface &&
96 eglGetCurrentSurface(EGL_DRAW) == _egl_surface &&
97 eglGetCurrentContext() == androidgsg->_context) {
102 if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, androidgsg->_context)) {
103 androiddisplay_cat.error() <<
"Failed to call eglMakeCurrent: " 113 androidgsg->reset_if_new();
115 if (mode == FM_render) {
117 clear_cube_map_selection();
121 return _gsg->begin_frame(current_thread);
131 end_frame_spam(mode);
132 nassertv(_gsg !=
nullptr);
134 if (mode == FM_render) {
139 _gsg->end_frame(current_thread);
141 if (mode == FM_render) {
143 clear_cube_map_selection();
156 if (_gsg !=
nullptr && _flip_ready) {
164 if (_egl_surface != EGL_NO_SURFACE) {
165 eglSwapBuffers(_egl_display, _egl_surface);
185 struct android_poll_source* source;
188 while ((looper_id = ALooper_pollAll(0,
nullptr, &events, (
void**)&source)) >= 0) {
190 if (source !=
nullptr) {
191 source->process(_app, source);
210 if (_pipe ==
nullptr) {
224 uint32_t add_flags = 0;
225 uint32_t del_flags = 0;
226 if (_properties.get_fullscreen()) {
227 add_flags |= AWINDOW_FLAG_FULLSCREEN;
229 del_flags |= AWINDOW_FLAG_FULLSCREEN;
231 ANativeActivity_setWindowFlags(_app->activity, add_flags, del_flags);
241 void AndroidGraphicsWindow::
245 if (_gsg !=
nullptr) {
249 GraphicsWindow::close_window();
251 nassertv(_app !=
nullptr);
252 if (_app->userData ==
this) {
253 _app->userData =
nullptr;
254 _app->onAppCmd =
nullptr;
255 _app->onInputEvent =
nullptr;
263 bool AndroidGraphicsWindow::
275 DCAST_INTO_R(androidgsg, _gsg,
false);
284 assert(_app !=
nullptr);
285 _app->userData =
this;
286 _app->onAppCmd = handle_command;
287 _app->onInputEvent = handle_input_event;
290 while (_app->window ==
nullptr) {
295 if (_egl_surface == EGL_NO_SURFACE) {
300 _properties.set_origin(0, 0);
301 _properties.set_cursor_hidden(
true);
302 _properties.set_undecorated(
true);
305 (_fb_properties, androidgsg->get_gl_renderer())) {
318 void AndroidGraphicsWindow::
320 if (_egl_surface != EGL_NO_SURFACE) {
321 if (!eglDestroySurface(_egl_display, _egl_surface)) {
322 androiddisplay_cat.error() <<
"Failed to destroy surface: " 325 _egl_surface = EGL_NO_SURFACE;
329 if (_gsg !=
nullptr) {
331 DCAST_INTO_V(androidgsg, _gsg);
339 bool AndroidGraphicsWindow::
342 DCAST_INTO_R(androidgsg, _gsg,
false);
345 ANativeWindow_setBuffersGeometry(_app->window, 0, 0, androidgsg->_format);
348 uint32_t add_flags = 0;
349 uint32_t del_flags = 0;
350 if (_properties.get_fullscreen()) {
351 add_flags |= AWINDOW_FLAG_FULLSCREEN;
353 del_flags |= AWINDOW_FLAG_FULLSCREEN;
355 ANativeActivity_setWindowFlags(_app->activity, add_flags, del_flags);
358 _egl_surface = eglCreateWindowSurface(_egl_display, androidgsg->_fbconfig, _app->window,
nullptr);
359 if (eglGetError() != EGL_SUCCESS) {
360 androiddisplay_cat.error()
361 <<
"Failed to create window surface.\n";
366 if (androidgsg->_context == EGL_NO_CONTEXT) {
373 if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, androidgsg->_context)) {
374 androiddisplay_cat.error() <<
"Failed to call eglMakeCurrent: " 382 androidgsg->reset_if_new();
383 if (!androidgsg->is_valid()) {
394 void AndroidGraphicsWindow::
395 handle_command(
struct android_app *app, int32_t command) {
397 if (window !=
nullptr) {
398 window->ns_handle_command(command);
405 void AndroidGraphicsWindow::
406 ns_handle_command(int32_t command) {
410 case APP_CMD_SAVE_STATE:
416 case APP_CMD_INIT_WINDOW:
418 if (_app->window !=
nullptr) {
420 properties.
set_size(ANativeWindow_getWidth(_app->window),
421 ANativeWindow_getHeight(_app->window));
423 system_changed_properties(properties);
426 case APP_CMD_CONFIG_CHANGED:
427 properties.
set_size(ANativeWindow_getWidth(_app->window),
428 ANativeWindow_getHeight(_app->window));
429 system_changed_properties(properties);
431 case APP_CMD_TERM_WINDOW:
434 system_changed_properties(properties);
436 case APP_CMD_WINDOW_RESIZED:
437 properties.
set_size(ANativeWindow_getWidth(_app->window),
438 ANativeWindow_getHeight(_app->window));
440 case APP_CMD_WINDOW_REDRAW_NEEDED:
442 case APP_CMD_CONTENT_RECT_CHANGED:
443 properties.
set_origin(_app->contentRect.left, _app->contentRect.top);
444 properties.
set_size(_app->contentRect.right - _app->contentRect.left,
445 _app->contentRect.bottom - _app->contentRect.top);
446 system_changed_properties(properties);
448 case APP_CMD_GAINED_FOCUS:
450 system_changed_properties(properties);
452 case APP_CMD_LOST_FOCUS:
454 system_changed_properties(properties);
456 case APP_CMD_DESTROY:
459 system_changed_properties(properties);
467 int32_t AndroidGraphicsWindow::
468 handle_input_event(
struct android_app* app, AInputEvent *event) {
471 int32_t event_type = AInputEvent_getType(event);
472 switch (event_type) {
473 case AINPUT_EVENT_TYPE_KEY:
474 return window->handle_key_event(event);
475 case AINPUT_EVENT_TYPE_MOTION:
476 return window->handle_motion_event(event);
484 int32_t AndroidGraphicsWindow::
485 handle_key_event(
const AInputEvent *event) {
507 int32_t keycode = AKeyEvent_getKeyCode(event);
510 if (button == ButtonHandle::none()) {
511 androiddisplay_cat.warning()
512 <<
"Unknown keycode: " << keycode <<
"\n";
517 int32_t action = AKeyEvent_getAction(event);
518 if (action == AKEY_EVENT_ACTION_DOWN) {
519 if (AKeyEvent_getRepeatCount(event) > 0) {
524 }
else if (action == AKEY_EVENT_ACTION_UP) {
535 int32_t AndroidGraphicsWindow::
536 handle_motion_event(
const AInputEvent *event) {
537 int32_t action = AMotionEvent_getAction(event);
538 action &= AMOTION_EVENT_ACTION_MASK;
540 if (action == AMOTION_EVENT_ACTION_DOWN ||
541 action == AMOTION_EVENT_ACTION_UP) {
544 int32_t button_state = AMotionEvent_getButtonState(event);
545 if (button_state == 0 && action == AMOTION_EVENT_ACTION_DOWN) {
546 button_state = AMOTION_EVENT_BUTTON_PRIMARY;
548 int32_t changed = _mouse_button_state ^ button_state;
550 if (changed & AMOTION_EVENT_BUTTON_PRIMARY) {
551 if (button_state & AMOTION_EVENT_BUTTON_PRIMARY) {
557 if (changed & AMOTION_EVENT_BUTTON_SECONDARY) {
558 if (button_state & AMOTION_EVENT_BUTTON_SECONDARY) {
564 _mouse_button_state = button_state;
568 float x = AMotionEvent_getX(event, 0) - _app->contentRect.left;
569 float y = AMotionEvent_getY(event, 0) - _app->contentRect.top;
581 map_button(int32_t keycode) {
583 case AKEYCODE_SOFT_LEFT:
584 case AKEYCODE_SOFT_RIGHT:
588 case AKEYCODE_ENDCALL:
614 case AKEYCODE_DPAD_UP:
615 return KeyboardButton::up();
616 case AKEYCODE_DPAD_DOWN:
617 return KeyboardButton::down();
618 case AKEYCODE_DPAD_LEFT:
619 return KeyboardButton::left();
620 case AKEYCODE_DPAD_RIGHT:
621 return KeyboardButton::right();
622 case AKEYCODE_DPAD_CENTER:
623 case AKEYCODE_VOLUME_UP:
624 case AKEYCODE_VOLUME_DOWN:
626 case AKEYCODE_CAMERA:
683 case AKEYCODE_PERIOD:
685 case AKEYCODE_ALT_LEFT:
686 return KeyboardButton::lalt();
687 case AKEYCODE_ALT_RIGHT:
688 return KeyboardButton::ralt();
689 case AKEYCODE_SHIFT_LEFT:
690 return KeyboardButton::lshift();
691 case AKEYCODE_SHIFT_RIGHT:
692 return KeyboardButton::rshift();
694 return KeyboardButton::tab();
696 return KeyboardButton::space();
698 case AKEYCODE_EXPLORER:
699 case AKEYCODE_ENVELOPE:
702 return KeyboardButton::enter();
704 return KeyboardButton::backspace();
709 case AKEYCODE_EQUALS:
711 case AKEYCODE_LEFT_BRACKET:
713 case AKEYCODE_RIGHT_BRACKET:
715 case AKEYCODE_BACKSLASH:
717 case AKEYCODE_SEMICOLON:
719 case AKEYCODE_APOSTROPHE:
726 case AKEYCODE_HEADSETHOOK:
732 return KeyboardButton::menu();
733 case AKEYCODE_NOTIFICATION:
734 case AKEYCODE_SEARCH:
735 case AKEYCODE_MEDIA_PLAY_PAUSE:
736 case AKEYCODE_MEDIA_STOP:
737 case AKEYCODE_MEDIA_NEXT:
738 case AKEYCODE_MEDIA_PREVIOUS:
739 case AKEYCODE_MEDIA_REWIND:
740 case AKEYCODE_MEDIA_FAST_FORWARD:
743 case AKEYCODE_PAGE_UP:
744 return KeyboardButton::page_up();
745 case AKEYCODE_PAGE_DOWN:
746 return KeyboardButton::page_down();
747 case AKEYCODE_PICTSYMBOLS:
748 case AKEYCODE_SWITCH_CHARSET:
749 case AKEYCODE_BUTTON_A:
750 case AKEYCODE_BUTTON_B:
751 case AKEYCODE_BUTTON_C:
752 case AKEYCODE_BUTTON_X:
753 case AKEYCODE_BUTTON_Y:
754 case AKEYCODE_BUTTON_Z:
755 case AKEYCODE_BUTTON_L1:
756 case AKEYCODE_BUTTON_R1:
757 case AKEYCODE_BUTTON_L2:
758 case AKEYCODE_BUTTON_R2:
759 case AKEYCODE_BUTTON_THUMBL:
760 case AKEYCODE_BUTTON_THUMBR:
761 case AKEYCODE_BUTTON_START:
762 case AKEYCODE_BUTTON_SELECT:
763 case AKEYCODE_BUTTON_MODE:
765 case AKEYCODE_ESCAPE:
766 return KeyboardButton::escape();
767 case AKEYCODE_FORWARD_DEL:
768 return KeyboardButton::del();
769 case AKEYCODE_CTRL_LEFT:
770 return KeyboardButton::lcontrol();
771 case AKEYCODE_CTRL_RIGHT:
772 return KeyboardButton::rcontrol();
773 case AKEYCODE_CAPS_LOCK:
774 return KeyboardButton::caps_lock();
775 case AKEYCODE_SCROLL_LOCK:
776 return KeyboardButton::scroll_lock();
777 case AKEYCODE_META_LEFT:
778 return KeyboardButton::lmeta();
779 case AKEYCODE_META_RIGHT:
780 return KeyboardButton::rmeta();
781 case AKEYCODE_FUNCTION:
784 return KeyboardButton::print_screen();
786 return KeyboardButton::pause();
787 case AKEYCODE_MOVE_HOME:
788 return KeyboardButton::home();
789 case AKEYCODE_MOVE_END:
790 return KeyboardButton::end();
791 case AKEYCODE_INSERT:
792 return KeyboardButton::insert();
794 return KeyboardButton::f1();
796 return KeyboardButton::f2();
798 return KeyboardButton::f3();
800 return KeyboardButton::f4();
802 return KeyboardButton::f5();
804 return KeyboardButton::f6();
806 return KeyboardButton::f7();
808 return KeyboardButton::f8();
810 return KeyboardButton::f9();
812 return KeyboardButton::f10();
814 return KeyboardButton::f11();
816 return KeyboardButton::f12();
817 case AKEYCODE_NUM_LOCK:
818 return KeyboardButton::num_lock();
822 return ButtonHandle::none();
bool is_any_specified() const
Returns true if any properties have been specified, false otherwise.
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
const FrameBufferProperties & get_fb_properties() const
Gets the FrameBufferProperties for all windows and buffers that use this GSG.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
set_size
Specifies the requested size of the window, in pixels.
virtual void end_flip()
This function will be called within the draw thread after begin_flip() has been called on all windows...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
set_minimized
Specifies whether the window should be created minimized (true), or normal (false).
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
An interface to manage Android windows and their appropriate EGL surfaces.
A window, fullscreen or on a desktop, into which a graphics device sends its output for interactive d...
has_fullscreen
Returns true if set_fullscreen() has been specified.
A container for the various kinds of properties we might ask to have on a graphics window before we o...
set_origin
Specifies the origin on the screen (in pixels, relative to the top-left corner) at which the window s...
const std::string get_egl_error_string(int error)
Returns the given EGL error as string.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is a base class for the various different classes that represent the result of a frame of render...
bool verify_hardware_software(const FrameBufferProperties &props, const std::string &renderer) const
Validates that the properties represent the desired kind of renderer (hardware or software).
clear_fullscreen
Removes the fullscreen specification from the properties.
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.
A thread; that is, a lightweight process.
A tiny specialization on GLESGraphicsStateGuardian to add some egl-specific information.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void destroy_context()
Destroys the context previously created by create_context.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool subsumes(const FrameBufferProperties &other) const
Returns true if this set of properties makes strictly greater or equal demands of the framebuffer tha...
This class is the main interface to controlling the render process.
get_fullscreen
Returns true if the window is in fullscreen mode.
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...
set_open
Specifies whether the window should be open.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
set_foreground
Specifies whether the window should be opened in the foreground (true), or left in the background (fa...
const FrameBufferProperties & get_fb_properties() const
Returns the framebuffer properties of the window.