|
|
@@ -0,0 +1,771 @@
|
|
|
+// Filename: androidGraphicsWindow.cxx
|
|
|
+// Created by: rdb (11Jan13)
|
|
|
+//
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+//
|
|
|
+// PANDA 3D SOFTWARE
|
|
|
+// Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
|
+//
|
|
|
+// All use of this software is subject to the terms of the revised BSD
|
|
|
+// license. You should have received a copy of this license along
|
|
|
+// with this source code in a file named "LICENSE."
|
|
|
+//
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+#include "androidGraphicsWindow.h"
|
|
|
+#include "androidGraphicsStateGuardian.h"
|
|
|
+#include "config_androiddisplay.h"
|
|
|
+#include "androidGraphicsPipe.h"
|
|
|
+
|
|
|
+#include "graphicsPipe.h"
|
|
|
+#include "keyboardButton.h"
|
|
|
+#include "mouseButton.h"
|
|
|
+#include "clockObject.h"
|
|
|
+#include "pStatTimer.h"
|
|
|
+#include "textEncoder.h"
|
|
|
+#include "throw_event.h"
|
|
|
+#include "nativeWindowHandle.h"
|
|
|
+
|
|
|
+#include "android_native_app_glue.h"
|
|
|
+#include <android/window.h>
|
|
|
+#include <android/log.h>
|
|
|
+
|
|
|
+extern struct android_app* panda_android_app;
|
|
|
+
|
|
|
+TypeHandle AndroidGraphicsWindow::_type_handle;
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AndroidGraphicsWindow::Constructor
|
|
|
+// Access: Public
|
|
|
+// Description:
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+AndroidGraphicsWindow::
|
|
|
+AndroidGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
|
|
|
+ const string &name,
|
|
|
+ const FrameBufferProperties &fb_prop,
|
|
|
+ const WindowProperties &win_prop,
|
|
|
+ int flags,
|
|
|
+ GraphicsStateGuardian *gsg,
|
|
|
+ GraphicsOutput *host) :
|
|
|
+ GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
|
|
|
+{
|
|
|
+ AndroidGraphicsPipe *android_pipe;
|
|
|
+ DCAST_INTO_V(android_pipe, _pipe);
|
|
|
+
|
|
|
+ _egl_display = android_pipe->_egl_display;
|
|
|
+ _egl_surface = 0;
|
|
|
+
|
|
|
+ _app = panda_android_app;
|
|
|
+
|
|
|
+ GraphicsWindowInputDevice device =
|
|
|
+ GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse");
|
|
|
+ add_input_device(device);
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AndroidGraphicsWindow::Destructor
|
|
|
+// Access: Public, Virtual
|
|
|
+// Description:
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+AndroidGraphicsWindow::
|
|
|
+~AndroidGraphicsWindow() {
|
|
|
+ destroy_surface();
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AndroidGraphicsWindow::begin_frame
|
|
|
+// Access: Public, Virtual
|
|
|
+// Description: This function will be called within the draw thread
|
|
|
+// before beginning rendering for a given frame. It
|
|
|
+// should do whatever setup is required, and return true
|
|
|
+// if the frame should be rendered, or false if it
|
|
|
+// should be skipped.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+bool AndroidGraphicsWindow::
|
|
|
+begin_frame(FrameMode mode, Thread *current_thread) {
|
|
|
+ PStatTimer timer(_make_current_pcollector, current_thread);
|
|
|
+
|
|
|
+ begin_frame_spam(mode);
|
|
|
+ if (_gsg == (GraphicsStateGuardian *)NULL) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ //XXX not open yet.
|
|
|
+ if (_egl_surface == EGL_NO_SURFACE) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ AndroidGraphicsStateGuardian *androidgsg;
|
|
|
+ DCAST_INTO_R(androidgsg, _gsg, false);
|
|
|
+ {
|
|
|
+ if (eglGetCurrentDisplay() == _egl_display &&
|
|
|
+ eglGetCurrentSurface(EGL_READ) == _egl_surface &&
|
|
|
+ eglGetCurrentSurface(EGL_DRAW) == _egl_surface &&
|
|
|
+ eglGetCurrentContext() == androidgsg->_context) {
|
|
|
+ // No need to make the context current again. Short-circuit
|
|
|
+ // this possibly-expensive call.
|
|
|
+ } else {
|
|
|
+ // Need to set the context.
|
|
|
+ if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, androidgsg->_context)) {
|
|
|
+ androiddisplay_cat.error() << "Failed to call eglMakeCurrent: "
|
|
|
+ << get_egl_error_string(eglGetError()) << "\n";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Now that we have made the context current to a window, we can
|
|
|
+ // reset the GSG state if this is the first time it has been used.
|
|
|
+ // (We can't just call reset() when we construct the GSG, because
|
|
|
+ // reset() requires having a current context.)
|
|
|
+ androidgsg->reset_if_new();
|
|
|
+
|
|
|
+ if (mode == FM_render) {
|
|
|
+ // begin_render_texture();
|
|
|
+ clear_cube_map_selection();
|
|
|
+ }
|
|
|
+
|
|
|
+ _gsg->set_current_properties(&get_fb_properties());
|
|
|
+ return _gsg->begin_frame(current_thread);
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AndroidGraphicsWindow::end_frame
|
|
|
+// Access: Public, Virtual
|
|
|
+// Description: This function will be called within the draw thread
|
|
|
+// after rendering is completed for a given frame. It
|
|
|
+// should do whatever finalization is required.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void AndroidGraphicsWindow::
|
|
|
+end_frame(FrameMode mode, Thread *current_thread) {
|
|
|
+ end_frame_spam(mode);
|
|
|
+ nassertv(_gsg != (GraphicsStateGuardian *)NULL);
|
|
|
+
|
|
|
+ if (mode == FM_render) {
|
|
|
+ // end_render_texture();
|
|
|
+ copy_to_textures();
|
|
|
+ }
|
|
|
+
|
|
|
+ _gsg->end_frame(current_thread);
|
|
|
+
|
|
|
+ if (mode == FM_render) {
|
|
|
+ trigger_flip();
|
|
|
+ clear_cube_map_selection();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AndroidGraphicsWindow::end_flip
|
|
|
+// Access: Public, Virtual
|
|
|
+// Description: This function will be called within the draw thread
|
|
|
+// after begin_flip() has been called on all windows, to
|
|
|
+// finish the exchange of the front and back buffers.
|
|
|
+//
|
|
|
+// This should cause the window to wait for the flip, if
|
|
|
+// necessary.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void AndroidGraphicsWindow::
|
|
|
+end_flip() {
|
|
|
+ if (_gsg != (GraphicsStateGuardian *)NULL && _flip_ready) {
|
|
|
+
|
|
|
+ // It doesn't appear to be necessary to ensure the graphics
|
|
|
+ // context is current before flipping the windows, and insisting
|
|
|
+ // on doing so can be a significant performance hit.
|
|
|
+
|
|
|
+ //make_current();
|
|
|
+
|
|
|
+ if (_egl_surface != EGL_NO_SURFACE) {
|
|
|
+ eglSwapBuffers(_egl_display, _egl_surface);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ GraphicsWindow::end_flip();
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AndroidGraphicsWindow::process_events
|
|
|
+// Access: Public, Virtual
|
|
|
+// Description: Do whatever processing is necessary to ensure that
|
|
|
+// the window responds to user events. Also, honor any
|
|
|
+// requests recently made via request_properties()
|
|
|
+//
|
|
|
+// This function is called only within the window
|
|
|
+// thread.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void AndroidGraphicsWindow::
|
|
|
+process_events() {
|
|
|
+ GraphicsWindow::process_events();
|
|
|
+
|
|
|
+ // Read all pending events.
|
|
|
+ int looper_id;
|
|
|
+ int events;
|
|
|
+ struct android_poll_source* source;
|
|
|
+
|
|
|
+ // Loop until all events are read.
|
|
|
+ while ((looper_id = ALooper_pollAll(0, NULL, &events, (void**)&source)) >= 0) {
|
|
|
+ // Process this event.
|
|
|
+ if (source != NULL) {
|
|
|
+ source->process(_app, source);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AndroidGraphicsWindow::set_properties_now
|
|
|
+// Access: Public, Virtual
|
|
|
+// Description: Applies the requested set of properties to the
|
|
|
+// window, if possible, for instance to request a change
|
|
|
+// in size or minimization status.
|
|
|
+//
|
|
|
+// The window properties are applied immediately, rather
|
|
|
+// than waiting until the next frame. This implies that
|
|
|
+// this method may *only* be called from within the
|
|
|
+// window thread.
|
|
|
+//
|
|
|
+// The return value is true if the properties are set,
|
|
|
+// false if they are ignored. This is mainly useful for
|
|
|
+// derived classes to implement extensions to this
|
|
|
+// function.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void AndroidGraphicsWindow::
|
|
|
+set_properties_now(WindowProperties &properties) {
|
|
|
+ if (_pipe == (GraphicsPipe *)NULL) {
|
|
|
+ // If the pipe is null, we're probably closing down.
|
|
|
+ GraphicsWindow::set_properties_now(properties);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ GraphicsWindow::set_properties_now(properties);
|
|
|
+ if (!properties.is_any_specified()) {
|
|
|
+ // The base class has already handled this case.
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // There's not really much we can change on Android.
|
|
|
+ if (properties.has_fullscreen()) {
|
|
|
+ uint32_t add_flags = 0;
|
|
|
+ uint32_t del_flags = 0;
|
|
|
+ if (_properties.get_fullscreen()) {
|
|
|
+ add_flags |= AWINDOW_FLAG_FULLSCREEN;
|
|
|
+ } else {
|
|
|
+ del_flags |= AWINDOW_FLAG_FULLSCREEN;
|
|
|
+ }
|
|
|
+ ANativeActivity_setWindowFlags(_app->activity, add_flags, del_flags);
|
|
|
+
|
|
|
+ _properties.set_fullscreen(properties.get_fullscreen());
|
|
|
+ properties.clear_fullscreen();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AndroidGraphicsWindow::close_window
|
|
|
+// Access: Protected, Virtual
|
|
|
+// Description: Closes the window right now. Called from the window
|
|
|
+// thread.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void AndroidGraphicsWindow::
|
|
|
+close_window() {
|
|
|
+ destroy_surface();
|
|
|
+
|
|
|
+ if (_gsg != (GraphicsStateGuardian *)NULL) {
|
|
|
+ _gsg.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ GraphicsWindow::close_window();
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AndroidGraphicsWindow::open_window
|
|
|
+// Access: Protected, Virtual
|
|
|
+// Description: Opens the window right now. Called from the window
|
|
|
+// thread. Returns true if the window is successfully
|
|
|
+// opened, or false if there was a problem.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+bool AndroidGraphicsWindow::
|
|
|
+open_window() {
|
|
|
+ // GSG Creation/Initialization
|
|
|
+ AndroidGraphicsStateGuardian *androidgsg;
|
|
|
+ if (_gsg == 0) {
|
|
|
+ // There is no old gsg. Create a new one.
|
|
|
+ androidgsg = new AndroidGraphicsStateGuardian(_engine, _pipe, NULL);
|
|
|
+ androidgsg->choose_pixel_format(_fb_properties, false, false);
|
|
|
+ _gsg = androidgsg;
|
|
|
+ } else {
|
|
|
+ // If the old gsg has the wrong pixel format, create a
|
|
|
+ // new one that shares with the old gsg.
|
|
|
+ DCAST_INTO_R(androidgsg, _gsg, false);
|
|
|
+ if (!androidgsg->get_fb_properties().subsumes(_fb_properties)) {
|
|
|
+ androidgsg = new AndroidGraphicsStateGuardian(_engine, _pipe, androidgsg);
|
|
|
+ androidgsg->choose_pixel_format(_fb_properties, false, false);
|
|
|
+ _gsg = androidgsg;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Register the callbacks
|
|
|
+ assert(_app != NULL);
|
|
|
+ _app->userData = this;
|
|
|
+ _app->onAppCmd = handle_command;
|
|
|
+ _app->onInputEvent = handle_input_event;
|
|
|
+
|
|
|
+ // Wait until Android has opened the window.
|
|
|
+ while (_app->window == NULL) {
|
|
|
+ process_events();
|
|
|
+ }
|
|
|
+
|
|
|
+ // create_surface should have been called by now.
|
|
|
+ if (_egl_surface == EGL_NO_SURFACE) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Set some other properties.
|
|
|
+ _properties.set_origin(0, 0);
|
|
|
+ _properties.set_cursor_hidden(true);
|
|
|
+ _properties.set_undecorated(true);
|
|
|
+
|
|
|
+ if (!androidgsg->get_fb_properties().verify_hardware_software
|
|
|
+ (_fb_properties, androidgsg->get_gl_renderer())) {
|
|
|
+ close_window();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ //_fb_properties = androidgsg->get_fb_properties();
|
|
|
+
|
|
|
+ androiddisplay_cat.error() << "open_window done\n";
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AndroidGraphicsWindow::destroy_surface
|
|
|
+// Access: Protected, Virtual
|
|
|
+// Description: Terminates the EGL surface.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void AndroidGraphicsWindow::
|
|
|
+destroy_surface() {
|
|
|
+ if (_egl_surface != EGL_NO_SURFACE) {
|
|
|
+ if (!eglDestroySurface(_egl_display, _egl_surface)) {
|
|
|
+ androiddisplay_cat.error() << "Failed to destroy surface: "
|
|
|
+ << get_egl_error_string(eglGetError()) << "\n";
|
|
|
+ }
|
|
|
+ _egl_surface = EGL_NO_SURFACE;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Destroy the current context.
|
|
|
+ if (_gsg != (GraphicsStateGuardian *)NULL) {
|
|
|
+ AndroidGraphicsStateGuardian *androidgsg;
|
|
|
+ DCAST_INTO_V(androidgsg, _gsg);
|
|
|
+ androidgsg->destroy_context();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AndroidGraphicsWindow::create_surface
|
|
|
+// Access: Protected, Virtual
|
|
|
+// Description: Creates the EGL surface.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+bool AndroidGraphicsWindow::
|
|
|
+create_surface() {
|
|
|
+ AndroidGraphicsStateGuardian *androidgsg;
|
|
|
+ DCAST_INTO_R(androidgsg, _gsg, false);
|
|
|
+
|
|
|
+ // Reconfigure the window buffers to match that of our framebuffer config.
|
|
|
+ ANativeWindow_setBuffersGeometry(_app->window, 0, 0, androidgsg->_format);
|
|
|
+
|
|
|
+ // Set any window flags
|
|
|
+ uint32_t add_flags = 0;
|
|
|
+ uint32_t del_flags = 0;
|
|
|
+ if (_properties.get_fullscreen()) {
|
|
|
+ add_flags |= AWINDOW_FLAG_FULLSCREEN;
|
|
|
+ } else {
|
|
|
+ del_flags |= AWINDOW_FLAG_FULLSCREEN;
|
|
|
+ }
|
|
|
+ ANativeActivity_setWindowFlags(_app->activity, add_flags, del_flags);
|
|
|
+
|
|
|
+ // Create the EGL surface.
|
|
|
+ _egl_surface = eglCreateWindowSurface(_egl_display, androidgsg->_fbconfig, _app->window, NULL);
|
|
|
+ if (eglGetError() != EGL_SUCCESS) {
|
|
|
+ androiddisplay_cat.error()
|
|
|
+ << "Failed to create window surface.\n";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Create a context.
|
|
|
+ if (androidgsg->_context == EGL_NO_CONTEXT) {
|
|
|
+ androiddisplay_cat.error() << "creating context\n";
|
|
|
+ if (!androidgsg->create_context()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Switch to our newly created context.
|
|
|
+ if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, androidgsg->_context)) {
|
|
|
+ androiddisplay_cat.error() << "Failed to call eglMakeCurrent: "
|
|
|
+ << get_egl_error_string(eglGetError()) << "\n";
|
|
|
+ }
|
|
|
+
|
|
|
+ // Query the size of the surface.
|
|
|
+ //EGLint width, height;
|
|
|
+ //eglQuerySurface(_egl_display, _egl_surface, EGL_WIDTH, &width);
|
|
|
+ //eglQuerySurface(_egl_display, _egl_surface, EGL_HEIGHT, &height);
|
|
|
+
|
|
|
+ androidgsg->reset_if_new();
|
|
|
+ if (!androidgsg->is_valid()) {
|
|
|
+ close_window();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AndroidGraphicsWindow::handle_command
|
|
|
+// Access: Private, Static
|
|
|
+// Description: Android app sends a command from the main thread.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void AndroidGraphicsWindow::
|
|
|
+handle_command(struct android_app *app, int32_t command) {
|
|
|
+ AndroidGraphicsWindow* window = (AndroidGraphicsWindow*) app->userData;
|
|
|
+ window->ns_handle_command(command);
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AndroidGraphicsWindow::ns_handle_command
|
|
|
+// Access: Private
|
|
|
+// Description: Android app sends a command from the main thread.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void AndroidGraphicsWindow::
|
|
|
+ns_handle_command(int32_t command) {
|
|
|
+ WindowProperties properties;
|
|
|
+
|
|
|
+ switch (command) {
|
|
|
+ case APP_CMD_SAVE_STATE:
|
|
|
+ // The system has asked us to save our current state. Do so.
|
|
|
+ //engine->app->savedState = malloc(sizeof(struct saved_state));
|
|
|
+ //*((struct saved_state*)engine->app->savedState) = engine->state;
|
|
|
+ //engine->app->savedStateSize = sizeof(struct saved_state);
|
|
|
+ break;
|
|
|
+ case APP_CMD_INIT_WINDOW:
|
|
|
+ // The window is being shown, get it ready.
|
|
|
+ if (_app->window != NULL) {
|
|
|
+ create_surface();
|
|
|
+ properties.set_minimized(false);
|
|
|
+ system_changed_properties(properties);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case APP_CMD_TERM_WINDOW:
|
|
|
+ destroy_surface();
|
|
|
+ properties.set_minimized(true);
|
|
|
+ system_changed_properties(properties);
|
|
|
+ break;
|
|
|
+ case APP_CMD_WINDOW_RESIZED:
|
|
|
+ break;
|
|
|
+ case APP_CMD_WINDOW_REDRAW_NEEDED:
|
|
|
+ break;
|
|
|
+ case APP_CMD_CONTENT_RECT_CHANGED:
|
|
|
+ properties.set_origin(_app->contentRect.left, _app->contentRect.top);
|
|
|
+ properties.set_size(_app->contentRect.right - _app->contentRect.left,
|
|
|
+ _app->contentRect.bottom - _app->contentRect.top);
|
|
|
+ system_changed_properties(properties);
|
|
|
+ break;
|
|
|
+ case APP_CMD_GAINED_FOCUS:
|
|
|
+ properties.set_foreground(true);
|
|
|
+ system_changed_properties(properties);
|
|
|
+ break;
|
|
|
+ case APP_CMD_LOST_FOCUS:
|
|
|
+ properties.set_foreground(false);
|
|
|
+ system_changed_properties(properties);
|
|
|
+ break;
|
|
|
+ case APP_CMD_DESTROY:
|
|
|
+ close_window();
|
|
|
+ properties.set_open(false);
|
|
|
+ system_changed_properties(properties);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AndroidGraphicsWindow::handle_input_event
|
|
|
+// Access: Private, Static
|
|
|
+// Description: Processes an input event. Returns 1 if the event
|
|
|
+// was handled, 0 otherwise.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+int32_t AndroidGraphicsWindow::
|
|
|
+handle_input_event(struct android_app* app, AInputEvent *event) {
|
|
|
+ AndroidGraphicsWindow* window = (AndroidGraphicsWindow*) app->userData;
|
|
|
+
|
|
|
+ int32_t event_type = AInputEvent_getType(event);
|
|
|
+ switch (event_type) {
|
|
|
+ case AINPUT_EVENT_TYPE_KEY:
|
|
|
+ return window->handle_key_event(event);
|
|
|
+ case AINPUT_EVENT_TYPE_MOTION:
|
|
|
+ return window->handle_motion_event(event);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AndroidGraphicsWindow::handle_keystroke
|
|
|
+// Access: Private
|
|
|
+// Description: Processes a key event.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+int32_t AndroidGraphicsWindow::
|
|
|
+handle_key_event(const AInputEvent *event) {
|
|
|
+ /*
|
|
|
+ int32_t meta = AKeyEvent_getMetaState(event);
|
|
|
+ if (meta | AMETA_ALT_ON) {
|
|
|
+ _input_devices[0].button_down(KeyboardButton.alt());
|
|
|
+ }
|
|
|
+ if (meta | AMETA_ALT_LEFT_ON) {
|
|
|
+ _input_devices[0].button_down(KeyboardButton.lalt());
|
|
|
+ }
|
|
|
+ if (meta | AMETA_ALT_RIGHT_ON) {
|
|
|
+ _input_devices[0].button_down(KeyboardButton.ralt());
|
|
|
+ }
|
|
|
+ if (meta | AMETA_SHIFT_ON) {
|
|
|
+ _input_devices[0].button_down(KeyboardButton.shift());
|
|
|
+ }
|
|
|
+ if (meta | AMETA_SHIFT_LEFT_ON) {
|
|
|
+ _input_devices[0].button_down(KeyboardButton.lshift());
|
|
|
+ }
|
|
|
+ if (meta | AMETA_SHIFT_RIGHT_ON) {
|
|
|
+ _input_devices[0].button_down(KeyboardButton.rshift());
|
|
|
+ }*/
|
|
|
+
|
|
|
+ int32_t keycode = AKeyEvent_getKeyCode(event);
|
|
|
+ ButtonHandle button = map_button(keycode);
|
|
|
+
|
|
|
+ if (button == ButtonHandle::none()) {
|
|
|
+ androiddisplay_cat.warning()
|
|
|
+ << "Unknown keycode: " << keycode << "\n";
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Is it an up or down event?
|
|
|
+ int32_t action = AKeyEvent_getAction(event);
|
|
|
+ if (action == AKEY_EVENT_ACTION_DOWN) {
|
|
|
+ _input_devices[0].button_down(button);
|
|
|
+ } else if (action == AKEY_EVENT_ACTION_UP) {
|
|
|
+ _input_devices[0].button_up(button);
|
|
|
+ }
|
|
|
+ //TODO getRepeatCount, ACTION_MULTIPLE
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AndroidGraphicsWindow::handle_motion_event
|
|
|
+// Access: Private
|
|
|
+// Description: Processes a motion event.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+int32_t AndroidGraphicsWindow::
|
|
|
+handle_motion_event(const AInputEvent *event) {
|
|
|
+ int32_t action = AMotionEvent_getAction(event);
|
|
|
+ action &= AMOTION_EVENT_ACTION_MASK;
|
|
|
+
|
|
|
+ if (action == AMOTION_EVENT_ACTION_DOWN) {
|
|
|
+ _input_devices[0].button_down(MouseButton::one());
|
|
|
+ } else if (action == AMOTION_EVENT_ACTION_UP) {
|
|
|
+ _input_devices[0].button_up(MouseButton::one());
|
|
|
+ }
|
|
|
+
|
|
|
+ float x = AMotionEvent_getX(event, 0);
|
|
|
+ float y = AMotionEvent_getY(event, 0);
|
|
|
+
|
|
|
+ _input_devices[0].set_pointer_in_window(x, y);
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AndroidGraphicsWindow::map_button
|
|
|
+// Access: Private
|
|
|
+// Description: Given an Android keycode, returns an appropriate
|
|
|
+// ButtonHandle object, or ButtonHandle::none() if
|
|
|
+// a matching ButtonHandle does not exist.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+ButtonHandle AndroidGraphicsWindow::
|
|
|
+map_button(int32_t keycode) {
|
|
|
+ switch (keycode) {
|
|
|
+ case AKEYCODE_SOFT_LEFT:
|
|
|
+ case AKEYCODE_SOFT_RIGHT:
|
|
|
+ case AKEYCODE_HOME:
|
|
|
+ case AKEYCODE_BACK:
|
|
|
+ case AKEYCODE_CALL:
|
|
|
+ case AKEYCODE_ENDCALL:
|
|
|
+ break;
|
|
|
+ case AKEYCODE_0:
|
|
|
+ return KeyboardButton::ascii_key('0');
|
|
|
+ case AKEYCODE_1:
|
|
|
+ return KeyboardButton::ascii_key('1');
|
|
|
+ case AKEYCODE_2:
|
|
|
+ return KeyboardButton::ascii_key('2');
|
|
|
+ case AKEYCODE_3:
|
|
|
+ return KeyboardButton::ascii_key('3');
|
|
|
+ case AKEYCODE_4:
|
|
|
+ return KeyboardButton::ascii_key('4');
|
|
|
+ case AKEYCODE_5:
|
|
|
+ return KeyboardButton::ascii_key('5');
|
|
|
+ case AKEYCODE_6:
|
|
|
+ return KeyboardButton::ascii_key('6');
|
|
|
+ case AKEYCODE_7:
|
|
|
+ return KeyboardButton::ascii_key('7');
|
|
|
+ case AKEYCODE_8:
|
|
|
+ return KeyboardButton::ascii_key('8');
|
|
|
+ case AKEYCODE_9:
|
|
|
+ return KeyboardButton::ascii_key('9');
|
|
|
+ case AKEYCODE_STAR:
|
|
|
+ return KeyboardButton::ascii_key('*');
|
|
|
+ case AKEYCODE_POUND:
|
|
|
+ return KeyboardButton::ascii_key('#');
|
|
|
+ case AKEYCODE_DPAD_UP:
|
|
|
+ return KeyboardButton::up();
|
|
|
+ case AKEYCODE_DPAD_DOWN:
|
|
|
+ return KeyboardButton::down();
|
|
|
+ case AKEYCODE_DPAD_LEFT:
|
|
|
+ return KeyboardButton::left();
|
|
|
+ case AKEYCODE_DPAD_RIGHT:
|
|
|
+ return KeyboardButton::right();
|
|
|
+ case AKEYCODE_DPAD_CENTER:
|
|
|
+ case AKEYCODE_VOLUME_UP:
|
|
|
+ case AKEYCODE_VOLUME_DOWN:
|
|
|
+ case AKEYCODE_POWER:
|
|
|
+ case AKEYCODE_CAMERA:
|
|
|
+ case AKEYCODE_CLEAR:
|
|
|
+ break;
|
|
|
+ case AKEYCODE_A:
|
|
|
+ return KeyboardButton::ascii_key('a');
|
|
|
+ case AKEYCODE_B:
|
|
|
+ return KeyboardButton::ascii_key('b');
|
|
|
+ case AKEYCODE_C:
|
|
|
+ return KeyboardButton::ascii_key('c');
|
|
|
+ case AKEYCODE_D:
|
|
|
+ return KeyboardButton::ascii_key('d');
|
|
|
+ case AKEYCODE_E:
|
|
|
+ return KeyboardButton::ascii_key('e');
|
|
|
+ case AKEYCODE_F:
|
|
|
+ return KeyboardButton::ascii_key('f');
|
|
|
+ case AKEYCODE_G:
|
|
|
+ return KeyboardButton::ascii_key('g');
|
|
|
+ case AKEYCODE_H:
|
|
|
+ return KeyboardButton::ascii_key('h');
|
|
|
+ case AKEYCODE_I:
|
|
|
+ return KeyboardButton::ascii_key('i');
|
|
|
+ case AKEYCODE_J:
|
|
|
+ return KeyboardButton::ascii_key('j');
|
|
|
+ case AKEYCODE_K:
|
|
|
+ return KeyboardButton::ascii_key('k');
|
|
|
+ case AKEYCODE_L:
|
|
|
+ return KeyboardButton::ascii_key('l');
|
|
|
+ case AKEYCODE_M:
|
|
|
+ return KeyboardButton::ascii_key('m');
|
|
|
+ case AKEYCODE_N:
|
|
|
+ return KeyboardButton::ascii_key('n');
|
|
|
+ case AKEYCODE_O:
|
|
|
+ return KeyboardButton::ascii_key('o');
|
|
|
+ case AKEYCODE_P:
|
|
|
+ return KeyboardButton::ascii_key('p');
|
|
|
+ case AKEYCODE_Q:
|
|
|
+ return KeyboardButton::ascii_key('q');
|
|
|
+ case AKEYCODE_R:
|
|
|
+ return KeyboardButton::ascii_key('r');
|
|
|
+ case AKEYCODE_S:
|
|
|
+ return KeyboardButton::ascii_key('s');
|
|
|
+ case AKEYCODE_T:
|
|
|
+ return KeyboardButton::ascii_key('t');
|
|
|
+ case AKEYCODE_U:
|
|
|
+ return KeyboardButton::ascii_key('u');
|
|
|
+ case AKEYCODE_V:
|
|
|
+ return KeyboardButton::ascii_key('v');
|
|
|
+ case AKEYCODE_W:
|
|
|
+ return KeyboardButton::ascii_key('w');
|
|
|
+ case AKEYCODE_X:
|
|
|
+ return KeyboardButton::ascii_key('x');
|
|
|
+ case AKEYCODE_Y:
|
|
|
+ return KeyboardButton::ascii_key('y');
|
|
|
+ case AKEYCODE_Z:
|
|
|
+ return KeyboardButton::ascii_key('z');
|
|
|
+ case AKEYCODE_COMMA:
|
|
|
+ return KeyboardButton::ascii_key(',');
|
|
|
+ case AKEYCODE_PERIOD:
|
|
|
+ return KeyboardButton::ascii_key('.');
|
|
|
+ case AKEYCODE_ALT_LEFT:
|
|
|
+ return KeyboardButton::lalt();
|
|
|
+ case AKEYCODE_ALT_RIGHT:
|
|
|
+ return KeyboardButton::ralt();
|
|
|
+ case AKEYCODE_SHIFT_LEFT:
|
|
|
+ return KeyboardButton::lshift();
|
|
|
+ case AKEYCODE_SHIFT_RIGHT:
|
|
|
+ return KeyboardButton::rshift();
|
|
|
+ case AKEYCODE_TAB:
|
|
|
+ return KeyboardButton::tab();
|
|
|
+ case AKEYCODE_SPACE:
|
|
|
+ return KeyboardButton::space();
|
|
|
+ case AKEYCODE_SYM:
|
|
|
+ case AKEYCODE_EXPLORER:
|
|
|
+ case AKEYCODE_ENVELOPE:
|
|
|
+ break;
|
|
|
+ case AKEYCODE_ENTER:
|
|
|
+ return KeyboardButton::enter();
|
|
|
+ case AKEYCODE_DEL:
|
|
|
+ return KeyboardButton::del();
|
|
|
+ case AKEYCODE_GRAVE:
|
|
|
+ return KeyboardButton::ascii_key('`');
|
|
|
+ case AKEYCODE_MINUS:
|
|
|
+ return KeyboardButton::ascii_key('-');
|
|
|
+ case AKEYCODE_EQUALS:
|
|
|
+ return KeyboardButton::ascii_key('=');
|
|
|
+ case AKEYCODE_LEFT_BRACKET:
|
|
|
+ return KeyboardButton::ascii_key('[');
|
|
|
+ case AKEYCODE_RIGHT_BRACKET:
|
|
|
+ return KeyboardButton::ascii_key(']');
|
|
|
+ case AKEYCODE_BACKSLASH:
|
|
|
+ return KeyboardButton::ascii_key('\\');
|
|
|
+ case AKEYCODE_SEMICOLON:
|
|
|
+ return KeyboardButton::ascii_key(';');
|
|
|
+ case AKEYCODE_APOSTROPHE:
|
|
|
+ return KeyboardButton::ascii_key('\'');
|
|
|
+ case AKEYCODE_SLASH:
|
|
|
+ return KeyboardButton::ascii_key('/');
|
|
|
+ case AKEYCODE_AT:
|
|
|
+ return KeyboardButton::ascii_key('@');
|
|
|
+ case AKEYCODE_NUM:
|
|
|
+ case AKEYCODE_HEADSETHOOK:
|
|
|
+ case AKEYCODE_FOCUS:
|
|
|
+ break;
|
|
|
+ case AKEYCODE_PLUS:
|
|
|
+ return KeyboardButton::ascii_key('+');
|
|
|
+ case AKEYCODE_MENU:
|
|
|
+ case AKEYCODE_NOTIFICATION:
|
|
|
+ case AKEYCODE_SEARCH:
|
|
|
+ case AKEYCODE_MEDIA_PLAY_PAUSE:
|
|
|
+ case AKEYCODE_MEDIA_STOP:
|
|
|
+ case AKEYCODE_MEDIA_NEXT:
|
|
|
+ case AKEYCODE_MEDIA_PREVIOUS:
|
|
|
+ case AKEYCODE_MEDIA_REWIND:
|
|
|
+ case AKEYCODE_MEDIA_FAST_FORWARD:
|
|
|
+ case AKEYCODE_MUTE:
|
|
|
+ break;
|
|
|
+ case AKEYCODE_PAGE_UP:
|
|
|
+ return KeyboardButton::page_up();
|
|
|
+ case AKEYCODE_PAGE_DOWN:
|
|
|
+ return KeyboardButton::page_down();
|
|
|
+ case AKEYCODE_PICTSYMBOLS:
|
|
|
+ case AKEYCODE_SWITCH_CHARSET:
|
|
|
+ case AKEYCODE_BUTTON_A:
|
|
|
+ case AKEYCODE_BUTTON_B:
|
|
|
+ case AKEYCODE_BUTTON_C:
|
|
|
+ case AKEYCODE_BUTTON_X:
|
|
|
+ case AKEYCODE_BUTTON_Y:
|
|
|
+ case AKEYCODE_BUTTON_Z:
|
|
|
+ case AKEYCODE_BUTTON_L1:
|
|
|
+ case AKEYCODE_BUTTON_R1:
|
|
|
+ case AKEYCODE_BUTTON_L2:
|
|
|
+ case AKEYCODE_BUTTON_R2:
|
|
|
+ case AKEYCODE_BUTTON_THUMBL:
|
|
|
+ case AKEYCODE_BUTTON_THUMBR:
|
|
|
+ case AKEYCODE_BUTTON_START:
|
|
|
+ case AKEYCODE_BUTTON_SELECT:
|
|
|
+ case AKEYCODE_BUTTON_MODE:
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return ButtonHandle::none();
|
|
|
+}
|