open_toontown_panda3d/panda/src/androiddisplay/androidGraphicsWindow.cxx

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();
}