open_toontown_panda3d/panda/src/androiddisplay/androidGraphicsWindow.cxx

772 lines
25 KiB
C++

// 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) - _app->contentRect.left;
float y = AMotionEvent_getY(event, 0) - _app->contentRect.top;
_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();
}