blob: 96c973ae242f26c929536def45dbc55998353372 [file] [log] [blame]
/* Copyright (C) 2006-2010 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
#include "android/skin/ui.h"
#include "android/skin/file.h"
#include "android/skin/image.h"
#include "android/skin/keyboard.h"
#include "android/skin/rect.h"
#include "android/skin/trackball.h"
#include "android/skin/window.h"
#include "android/utils/bufprint.h"
#include "android/utils/debug.h"
#include "android/utils/system.h"
#include <stdbool.h>
#include <stdio.h>
#ifdef _WIN32
#include <windows.h>
#endif
#define D(...) do { if (VERBOSE_CHECK(init)) dprint(__VA_ARGS__); } while (0)
#define DE(...) do { if (VERBOSE_CHECK(keys)) dprint(__VA_ARGS__); } while (0)
struct SkinUI {
SkinUIParams ui_params;
const SkinUIFuncs* ui_funcs;
SkinFile* layout_file;
SkinLayout* layout;
SkinKeyboard* keyboard;
SkinWindow* window;
bool show_trackball;
SkinTrackBall* trackball;
int lcd_brightness;
SkinImage* onion;
SkinRotation onion_rotation;
int onion_alpha;
};
static void _skin_ui_handle_key_command(void* opaque,
SkinKeyCommand command,
int down);
static SkinLayout* skin_file_select_layout(SkinLayout* layouts,
const char* layout_name) {
if (!layout_name) return layouts;
SkinLayout* currLayout = layouts;
while (currLayout) {
if (currLayout->name && !strcmp(currLayout->name, layout_name)) {
return currLayout;
}
currLayout = currLayout->next;
}
return layouts;
}
SkinUI* skin_ui_create(SkinFile* layout_file, const char* initial_orientation,
const SkinUIFuncs* ui_funcs,
const SkinUIParams* ui_params) {
SkinUI* ui;
ANEW0(ui);
ui->layout_file = layout_file;
ui->layout = skin_file_select_layout(layout_file->layouts, initial_orientation);
ui->ui_funcs = ui_funcs;
ui->ui_params = ui_params[0];
ui->keyboard = skin_keyboard_create(ui->ui_params.keyboard_charmap,
ui->ui_params.keyboard_raw_keys,
ui_funcs->keyboard_flush);
ui->window = NULL;
skin_keyboard_enable(ui->keyboard, 1);
skin_keyboard_on_command(ui->keyboard, _skin_ui_handle_key_command, ui);
ui->window = skin_window_create(ui->layout,
ui->ui_params.window_x,
ui->ui_params.window_y,
ui->ui_params.window_scale,
0,
ui->ui_funcs->window_funcs);
if (!ui->window) {
skin_ui_free(ui);
return NULL;
}
if (ui->ui_params.enable_trackball) {
ui->trackball = skin_trackball_create(ui->ui_funcs->trackball_params);
skin_window_set_trackball(ui->window, ui->trackball);
}
ui->lcd_brightness = 128; /* 50% */
skin_window_set_lcd_brightness(ui->window, ui->lcd_brightness );
if (ui->onion) {
skin_window_set_onion(ui->window,
ui->onion,
ui->onion_rotation,
ui->onion_alpha);
}
skin_ui_reset_title(ui);
skin_window_enable_touch(ui->window, ui->ui_params.enable_touch);
skin_window_enable_dpad(ui->window, ui->ui_params.enable_dpad);
skin_window_enable_qwerty(ui->window, ui->ui_params.enable_keyboard);
skin_window_enable_trackball(ui->window, ui->ui_params.enable_trackball);
return ui;
}
void skin_ui_free(SkinUI* ui) {
if (ui->window) {
skin_window_free(ui->window);
ui->window = NULL;
}
if (ui->trackball) {
skin_trackball_destroy(ui->trackball);
ui->trackball = NULL;
}
if (ui->keyboard) {
skin_keyboard_free(ui->keyboard);
ui->keyboard = NULL;
}
skin_image_unref(&ui->onion);
ui->layout = NULL;
AFREE(ui);
}
void skin_ui_set_lcd_brightness(SkinUI* ui, int lcd_brightness) {
ui->lcd_brightness = lcd_brightness;
if (ui->window) {
skin_window_set_lcd_brightness(ui->window, lcd_brightness);
}
}
void skin_ui_set_scale(SkinUI* ui, double scale) {
if (ui->window) {
skin_window_set_scale(ui->window, scale);
}
}
void skin_ui_reset_title(SkinUI* ui) {
char temp[128], *p=temp, *end = p + sizeof(temp);
if (ui->window == NULL)
return;
if (ui->show_trackball) {
SkinKeyBinding bindings[SKIN_KEY_COMMAND_MAX_BINDINGS];
int count = skin_keyset_get_bindings(skin_keyset_get_default(),
SKIN_KEY_COMMAND_TOGGLE_TRACKBALL,
bindings);
if (count > 0) {
int nn;
p = bufprint(p, end, "Press ");
for (nn = 0; nn < count; nn++) {
if (nn > 0) {
if (nn < count-1)
p = bufprint(p, end, ", ");
else
p = bufprint(p, end, " or ");
}
p = bufprint(p, end, "%s",
skin_key_pair_to_string(bindings[nn].sym,
bindings[nn].mod));
}
p = bufprint(p, end, " to leave trackball mode. ");
}
}
p = bufprint(p, end, "%s", ui->ui_params.window_name);
skin_window_set_title(ui->window, temp);
}
void skin_ui_set_onion(SkinUI* ui,
SkinImage* onion,
SkinRotation onion_rotation,
int onion_alpha) {
if (onion) {
skin_image_ref(onion);
}
skin_image_unref(&ui->onion);
ui->onion = onion;
ui->onion_rotation = onion_rotation;
ui->onion_alpha = onion_alpha;
if (ui->window) {
skin_window_set_onion(ui->window,
onion,
onion_rotation,
onion_alpha);
}
}
/* used to respond to a given keyboard command shortcut
*/
static void
_skin_ui_handle_key_command(void* opaque, SkinKeyCommand command, int down)
{
SkinUI* ui = opaque;
static const struct { SkinKeyCommand cmd; SkinKeyCode kcode; } keycodes[] =
{
{ SKIN_KEY_COMMAND_BUTTON_CALL, kKeyCodeCall },
{ SKIN_KEY_COMMAND_BUTTON_HOME, kKeyCodeHome },
{ SKIN_KEY_COMMAND_BUTTON_BACK, kKeyCodeBack },
{ SKIN_KEY_COMMAND_BUTTON_HANGUP, kKeyCodeEndCall },
{ SKIN_KEY_COMMAND_BUTTON_POWER, kKeyCodePower },
{ SKIN_KEY_COMMAND_BUTTON_SEARCH, kKeyCodeSearch },
{ SKIN_KEY_COMMAND_BUTTON_MENU, kKeyCodeMenu },
{ SKIN_KEY_COMMAND_BUTTON_DPAD_UP, kKeyCodeDpadUp },
{ SKIN_KEY_COMMAND_BUTTON_DPAD_LEFT, kKeyCodeDpadLeft },
{ SKIN_KEY_COMMAND_BUTTON_DPAD_RIGHT, kKeyCodeDpadRight },
{ SKIN_KEY_COMMAND_BUTTON_DPAD_DOWN, kKeyCodeDpadDown },
{ SKIN_KEY_COMMAND_BUTTON_DPAD_CENTER, kKeyCodeDpadCenter },
{ SKIN_KEY_COMMAND_BUTTON_VOLUME_UP, kKeyCodeVolumeUp },
{ SKIN_KEY_COMMAND_BUTTON_VOLUME_DOWN, kKeyCodeVolumeDown },
{ SKIN_KEY_COMMAND_BUTTON_CAMERA, kKeyCodeCamera },
{ SKIN_KEY_COMMAND_BUTTON_TV, kKeyCodeTV },
{ SKIN_KEY_COMMAND_BUTTON_EPG, kKeyCodeEPG },
{ SKIN_KEY_COMMAND_BUTTON_DVR, kKeyCodeDVR },
{ SKIN_KEY_COMMAND_BUTTON_PREV, kKeyCodePrevious },
{ SKIN_KEY_COMMAND_BUTTON_NEXT, kKeyCodeNext },
{ SKIN_KEY_COMMAND_BUTTON_PLAY, kKeyCodePlay },
{ SKIN_KEY_COMMAND_BUTTON_PAUSE, kKeyCodePause },
{ SKIN_KEY_COMMAND_BUTTON_STOP, kKeyCodeStop },
{ SKIN_KEY_COMMAND_BUTTON_REWIND, kKeyCodeRewind },
{ SKIN_KEY_COMMAND_BUTTON_FFWD, kKeyCodeFastForward },
{ SKIN_KEY_COMMAND_BUTTON_BOOKMARKS, kKeyCodeBookmarks },
{ SKIN_KEY_COMMAND_BUTTON_WINDOW, kKeyCodeCycleWindows },
{ SKIN_KEY_COMMAND_BUTTON_CHANNELUP, kKeyCodeChannelUp },
{ SKIN_KEY_COMMAND_BUTTON_CHANNELDOWN, kKeyCodeChannelDown },
{ SKIN_KEY_COMMAND_NONE, 0 }
};
int nn;
for (nn = 0; keycodes[nn].kcode != 0; nn++) {
if (command == keycodes[nn].cmd) {
unsigned code = keycodes[nn].kcode;
ui->ui_funcs->keyboard_event(NULL, code, down);
return;
}
}
// for the show-trackball command, handle down events to enable, and
// up events to disable
if (command == SKIN_KEY_COMMAND_SHOW_TRACKBALL) {
ui->show_trackball = (down != 0);
skin_window_show_trackball(ui->window, ui->show_trackball);
return;
}
// only handle down events for the rest
if (down == 0)
return;
switch (command)
{
case SKIN_KEY_COMMAND_TOGGLE_NETWORK:
{
bool enabled = ui->ui_funcs->network_toggle();
D( "network is now %s", enabled ? "connected" : "disconnected");
}
break;
case SKIN_KEY_COMMAND_TOGGLE_FULLSCREEN:
if (ui->window) {
skin_window_toggle_fullscreen(ui->window);
}
break;
case SKIN_KEY_COMMAND_TOGGLE_TRACKBALL:
ui->show_trackball = !ui->show_trackball;
skin_window_show_trackball(ui->window, ui->show_trackball);
skin_ui_reset_title(ui);
break;
case SKIN_KEY_COMMAND_ONION_ALPHA_UP:
case SKIN_KEY_COMMAND_ONION_ALPHA_DOWN:
if (ui->onion) {
int alpha = ui->onion_alpha;
if (command == SKIN_KEY_COMMAND_ONION_ALPHA_UP)
alpha += 16;
else
alpha -= 16;
if (alpha > 256)
alpha = 256;
else if (alpha < 0)
alpha = 0;
ui->onion_alpha = alpha;
skin_window_set_onion(ui->window,
ui->onion,
ui->onion_rotation,
ui->onion_alpha);
skin_window_redraw(ui->window, NULL);
//dprint( "onion alpha set to %d (%.f %%)", alpha, alpha/2.56 );
}
break;
case SKIN_KEY_COMMAND_CHANGE_LAYOUT_PREV:
case SKIN_KEY_COMMAND_CHANGE_LAYOUT_NEXT:
{
SkinLayout* layout = NULL;
if (command == SKIN_KEY_COMMAND_CHANGE_LAYOUT_NEXT) {
layout = ui->layout->next;
if (layout == NULL)
layout = ui->layout_file->layouts;
}
else if (command == SKIN_KEY_COMMAND_CHANGE_LAYOUT_PREV) {
layout = ui->layout_file->layouts;
while (layout->next && layout->next != ui->layout)
layout = layout->next;
}
if (layout != NULL) {
ui->layout = layout;
skin_window_reset(ui->window, layout);
SkinRotation rotation = skin_layout_get_dpad_rotation(layout);
if (ui->keyboard)
skin_keyboard_set_rotation(ui->keyboard, rotation);
if (ui->trackball) {
skin_trackball_set_rotation(ui->trackball, rotation);
skin_window_set_trackball(ui->window, ui->trackball);
skin_window_show_trackball(ui->window, ui->show_trackball);
}
skin_window_set_lcd_brightness(ui->window, ui->lcd_brightness);
ui->ui_funcs->framebuffer_invalidate();
}
}
break;
default:
/* XXX: TODO ? */
;
}
}
bool skin_ui_process_events(SkinUI* ui) {
SkinEvent ev;
#ifdef _WIN32
if (ui->ui_params.win32_ignore_events) {
// In QT, Windows messages are received by the QT thread, which is
// the thread that creates all of its windows, so that should be
// okay. However, in GPU accelerated images, there's a native window
// that's created that represents the actual OpenGL output pane, and
// that window is created from this thread and not the QT thread. We
// need to keep its message queue from getting jammed up with
// messages, so we need to grab any messages we see and drop
// unnecessary ones on the floor.
MSG message;
PeekMessage(&message, NULL, 0, 0, PM_REMOVE);
}
#endif // _WIN32
while(skin_event_poll(&ev)) {
switch(ev.type) {
case kEventVideoExpose:
DE("EVENT: kEventVideoExpose\n");
skin_window_redraw(ui->window, NULL);
break;
case kEventKeyDown:
DE("EVENT: kEventKeyDown scancode=%d mod=0x%x\n",
ev.u.key.keycode, ev.u.key.mod);
skin_keyboard_process_event(ui->keyboard, &ev, 1);
break;
case kEventKeyUp:
DE("EVENT: kEventKeyUp scancode=%d mod=0x%x\n",
ev.u.key.keycode, ev.u.key.mod);
skin_keyboard_process_event(ui->keyboard, &ev, 0);
break;
case kEventTextInput:
DE("EVENT: kEventTextInput text=[%s] down=%s\n",
ev.u.text.text, ev.u.text.down ? "true" : "false");
skin_keyboard_process_event(ui->keyboard, &ev, ev.u.text.down);
break;
case kEventMouseMotion:
DE("EVENT: kEventMouseMotion x=%d y=%d xrel=%d yrel=%d button=%d\n",
ev.u.mouse.x, ev.u.mouse.y, ev.u.mouse.xrel, ev.u.mouse.yrel,
ev.u.mouse.button);
skin_window_process_event(ui->window, &ev);
break;
case kEventMouseButtonDown:
case kEventMouseButtonUp:
DE("EVENT: kEventMouseButton x=%d y=%d xrel=%d yrel=%d button=%d\n",
ev.u.mouse.x, ev.u.mouse.y, ev.u.mouse.xrel, ev.u.mouse.yrel,
ev.u.mouse.button);
{
int down = (ev.type == kEventMouseButtonDown);
if (ev.u.mouse.button == kMouseButtonScrollUp)
{
/* scroll-wheel simulates DPad up */
SkinKeyCode kcode;
kcode = skin_keycode_rotate(kKeyCodeDpadUp,
skin_layout_get_dpad_rotation(
ui->layout));
ui->ui_funcs->keyboard_event(NULL, kcode, down);
}
else if (ev.u.mouse.button == kMouseButtonScrollDown)
{
/* scroll-wheel simulates DPad down */
SkinKeyCode kcode;
kcode = skin_keycode_rotate(kKeyCodeDpadDown,
skin_layout_get_dpad_rotation(
ui->layout));
ui->ui_funcs->keyboard_event(NULL, kcode, down);
}
else if (ev.u.mouse.button == kMouseButtonLeft) {
skin_window_process_event(ui->window, &ev);
}
}
break;
case kEventQuit:
DE("EVENT: kEventQuit\n");
/* only save emulator config through clean exit */
return true;
}
}
skin_keyboard_flush(ui->keyboard);
return false;
}
void skin_ui_update_display(SkinUI* ui, int x, int y, int w, int h) {
if (ui->window) {
skin_window_update_display(ui->window, x, y, w, h);
}
}
void skin_ui_update_gpu_frame(SkinUI* ui, int w, int h, const void* pixels) {
if (ui->window) {
skin_window_update_gpu_frame(ui->window, w, h, pixels);
}
}
SkinLayout* skin_ui_get_current_layout(SkinUI* ui) {
return ui->layout;
}
void skin_ui_set_name(SkinUI* ui, const char* name) {
snprintf(ui->ui_params.window_name,
sizeof(ui->ui_params.window_name),
"%s",
name);
skin_ui_reset_title(ui);
}