824 lines
24 KiB
C++
824 lines
24 KiB
C++
/**
|
|
* 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."
|
|
*
|
|
* @file androidGraphicsWindow.cxx
|
|
* @author rdb
|
|
* @date 2013-01-11
|
|
*/
|
|
|
|
#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 IMPORT_CLASS struct android_app* panda_android_app;
|
|
|
|
TypeHandle AndroidGraphicsWindow::_type_handle;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
AndroidGraphicsWindow::
|
|
AndroidGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
|
|
const std::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),
|
|
_mouse_button_state(0)
|
|
{
|
|
AndroidGraphicsPipe *android_pipe;
|
|
DCAST_INTO_V(android_pipe, _pipe);
|
|
|
|
_egl_display = android_pipe->_egl_display;
|
|
_egl_surface = 0;
|
|
|
|
_app = panda_android_app;
|
|
|
|
PT(GraphicsWindowInputDevice) device = GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse");
|
|
add_input_device(device);
|
|
_input = device;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
AndroidGraphicsWindow::
|
|
~AndroidGraphicsWindow() {
|
|
destroy_surface();
|
|
}
|
|
|
|
/**
|
|
* 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 == nullptr) {
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* 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 != nullptr);
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 != nullptr && _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();
|
|
}
|
|
|
|
/**
|
|
* 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, nullptr, &events, (void**)&source)) >= 0) {
|
|
// Process this event.
|
|
if (source != nullptr) {
|
|
source->process(_app, source);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 == nullptr) {
|
|
// 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();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Closes the window right now. Called from the window thread.
|
|
*/
|
|
void AndroidGraphicsWindow::
|
|
close_window() {
|
|
destroy_surface();
|
|
|
|
if (_gsg != nullptr) {
|
|
_gsg.clear();
|
|
}
|
|
|
|
GraphicsWindow::close_window();
|
|
|
|
nassertv(_app != nullptr);
|
|
if (_app->userData == this) {
|
|
_app->userData = nullptr;
|
|
_app->onAppCmd = nullptr;
|
|
_app->onInputEvent = nullptr;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 CreationInitialization
|
|
AndroidGraphicsStateGuardian *androidgsg;
|
|
if (_gsg == 0) {
|
|
// There is no old gsg. Create a new one.
|
|
androidgsg = new AndroidGraphicsStateGuardian(_engine, _pipe, nullptr);
|
|
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 != nullptr);
|
|
_app->userData = this;
|
|
_app->onAppCmd = handle_command;
|
|
_app->onInputEvent = handle_input_event;
|
|
|
|
// Wait until Android has opened the window.
|
|
while (_app->window == nullptr) {
|
|
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();
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 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 != nullptr) {
|
|
AndroidGraphicsStateGuardian *androidgsg;
|
|
DCAST_INTO_V(androidgsg, _gsg);
|
|
androidgsg->destroy_context();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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, nullptr);
|
|
if (eglGetError() != EGL_SUCCESS) {
|
|
androiddisplay_cat.error()
|
|
<< "Failed to create window surface.\n";
|
|
return false;
|
|
}
|
|
|
|
// Create a context.
|
|
if (androidgsg->_context == EGL_NO_CONTEXT) {
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
if (window != nullptr) {
|
|
window->ns_handle_command(command);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 != nullptr) {
|
|
create_surface();
|
|
properties.set_size(ANativeWindow_getWidth(_app->window),
|
|
ANativeWindow_getHeight(_app->window));
|
|
properties.set_minimized(false);
|
|
system_changed_properties(properties);
|
|
}
|
|
break;
|
|
case APP_CMD_CONFIG_CHANGED:
|
|
properties.set_size(ANativeWindow_getWidth(_app->window),
|
|
ANativeWindow_getHeight(_app->window));
|
|
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:
|
|
properties.set_size(ANativeWindow_getWidth(_app->window),
|
|
ANativeWindow_getHeight(_app->window));
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* 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->button_down(KeyboardButton.alt());
|
|
}
|
|
if (meta | AMETA_ALT_LEFT_ON) {
|
|
_input->button_down(KeyboardButton.lalt());
|
|
}
|
|
if (meta | AMETA_ALT_RIGHT_ON) {
|
|
_input->button_down(KeyboardButton.ralt());
|
|
}
|
|
if (meta | AMETA_SHIFT_ON) {
|
|
_input->button_down(KeyboardButton.shift());
|
|
}
|
|
if (meta | AMETA_SHIFT_LEFT_ON) {
|
|
_input->button_down(KeyboardButton.lshift());
|
|
}
|
|
if (meta | AMETA_SHIFT_RIGHT_ON) {
|
|
_input->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) {
|
|
if (AKeyEvent_getRepeatCount(event) > 0) {
|
|
_input->button_resume_down(button);
|
|
} else {
|
|
_input->button_down(button);
|
|
}
|
|
} else if (action == AKEY_EVENT_ACTION_UP) {
|
|
_input->button_up(button);
|
|
}
|
|
// TODO AKEY_EVENT_ACTION_MULTIPLE
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* 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 ||
|
|
action == AMOTION_EVENT_ACTION_UP) {
|
|
// The up event doesn't let us know which button is up, so we need to
|
|
// keep track of the button state ourselves.
|
|
int32_t button_state = AMotionEvent_getButtonState(event);
|
|
if (button_state == 0 && action == AMOTION_EVENT_ACTION_DOWN) {
|
|
button_state = AMOTION_EVENT_BUTTON_PRIMARY;
|
|
}
|
|
int32_t changed = _mouse_button_state ^ button_state;
|
|
if (changed != 0) {
|
|
if (changed & AMOTION_EVENT_BUTTON_PRIMARY) {
|
|
if (button_state & AMOTION_EVENT_BUTTON_PRIMARY) {
|
|
_input->button_down(MouseButton::one());
|
|
} else {
|
|
_input->button_up(MouseButton::one());
|
|
}
|
|
}
|
|
if (changed & AMOTION_EVENT_BUTTON_SECONDARY) {
|
|
if (button_state & AMOTION_EVENT_BUTTON_SECONDARY) {
|
|
_input->button_down(MouseButton::three());
|
|
} else {
|
|
_input->button_up(MouseButton::three());
|
|
}
|
|
}
|
|
_mouse_button_state = button_state;
|
|
}
|
|
}
|
|
|
|
float x = AMotionEvent_getX(event, 0) - _app->contentRect.left;
|
|
float y = AMotionEvent_getY(event, 0) - _app->contentRect.top;
|
|
|
|
_input->set_pointer_in_window(x, y);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* 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::backspace();
|
|
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:
|
|
return KeyboardButton::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:
|
|
break;
|
|
case AKEYCODE_ESCAPE:
|
|
return KeyboardButton::escape();
|
|
case AKEYCODE_FORWARD_DEL:
|
|
return KeyboardButton::del();
|
|
case AKEYCODE_CTRL_LEFT:
|
|
return KeyboardButton::lcontrol();
|
|
case AKEYCODE_CTRL_RIGHT:
|
|
return KeyboardButton::rcontrol();
|
|
case AKEYCODE_CAPS_LOCK:
|
|
return KeyboardButton::caps_lock();
|
|
case AKEYCODE_SCROLL_LOCK:
|
|
return KeyboardButton::scroll_lock();
|
|
case AKEYCODE_META_LEFT:
|
|
return KeyboardButton::lmeta();
|
|
case AKEYCODE_META_RIGHT:
|
|
return KeyboardButton::rmeta();
|
|
case AKEYCODE_FUNCTION:
|
|
break;
|
|
case AKEYCODE_SYSRQ:
|
|
return KeyboardButton::print_screen();
|
|
case AKEYCODE_BREAK:
|
|
return KeyboardButton::pause();
|
|
case AKEYCODE_MOVE_HOME:
|
|
return KeyboardButton::home();
|
|
case AKEYCODE_MOVE_END:
|
|
return KeyboardButton::end();
|
|
case AKEYCODE_INSERT:
|
|
return KeyboardButton::insert();
|
|
case AKEYCODE_F1:
|
|
return KeyboardButton::f1();
|
|
case AKEYCODE_F2:
|
|
return KeyboardButton::f2();
|
|
case AKEYCODE_F3:
|
|
return KeyboardButton::f3();
|
|
case AKEYCODE_F4:
|
|
return KeyboardButton::f4();
|
|
case AKEYCODE_F5:
|
|
return KeyboardButton::f5();
|
|
case AKEYCODE_F6:
|
|
return KeyboardButton::f6();
|
|
case AKEYCODE_F7:
|
|
return KeyboardButton::f7();
|
|
case AKEYCODE_F8:
|
|
return KeyboardButton::f8();
|
|
case AKEYCODE_F9:
|
|
return KeyboardButton::f9();
|
|
case AKEYCODE_F10:
|
|
return KeyboardButton::f10();
|
|
case AKEYCODE_F11:
|
|
return KeyboardButton::f11();
|
|
case AKEYCODE_F12:
|
|
return KeyboardButton::f12();
|
|
case AKEYCODE_NUM_LOCK:
|
|
return KeyboardButton::num_lock();
|
|
default:
|
|
break;
|
|
}
|
|
return ButtonHandle::none();
|
|
}
|