blob: bc0907c7a836c7827231de7a04f6266bcc1b5e59 [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/emulator-window.h"
#include "android/android.h"
#include "android/framebuffer.h"
#include "android/globals.h"
#include "android/gpu_frame.h"
#include "android/hw-control.h"
#include "android/hw-sensors.h"
#include "android/looper.h"
#include "android/opengles.h"
#include "android/skin/keycode.h"
#include "android/skin/winsys.h"
#include "android/user-events.h"
#include "android/utils/debug.h"
#include "android/utils/bufprint.h"
#include "telephony/modem_driver.h"
#define D(...) do { if (VERBOSE_CHECK(init)) dprint(__VA_ARGS__); } while (0)
static double get_default_scale( AndroidOptions* opts );
/* EmulatorWindow structure instance. */
static EmulatorWindow qemulator[1];
// Set to 1 to use an EmuGL sub-window to display GpU content, or 0 to use
// the frame post callback to retrieve every frame from the GPU, which will
// be slower, except for software-based renderers.
static bool s_use_emugl_subwindow = 1;
static void emulator_window_refresh(EmulatorWindow* emulator);
extern void qemu_system_shutdown_request(void);
static void write_window_name(char* buff,
size_t buff_len,
int base_port,
const char* avd_name) {
snprintf(buff, buff_len, "%d:%s", base_port, avd_name);
}
static void
emulator_window_light_brightness(void* opaque, const char* light, int value)
{
EmulatorWindow* emulator = opaque;
VERBOSE_PRINT(hw_control,"%s: light='%s' value=%d ui=%p", __FUNCTION__, light, value, emulator->ui);
if (!strcmp(light, "lcd_backlight")) {
skin_ui_set_lcd_brightness(emulator->ui, value);
return;
}
}
static void emulator_window_trackball_event(int dx, int dy) {
user_event_mouse(dx, dy, 1, 0);
}
static void emulator_window_window_key_event(unsigned keycode, int down) {
user_event_key(keycode, down);
}
static void emulator_window_window_mouse_event(unsigned x,
unsigned y,
unsigned state) {
/* NOTE: the 0 is used in hw/android/goldfish/events_device.c to
* differentiate between a touch-screen and a trackball event
*/
user_event_mouse(x, y, 0, state);
}
static void emulator_window_window_generic_event(int event_type,
int event_code,
int event_value) {
user_event_generic(event_type, event_code, event_value);
/* XXX: hack, replace by better code here */
if (event_value != 0)
android_sensors_set_coarse_orientation(ANDROID_COARSE_PORTRAIT);
else
android_sensors_set_coarse_orientation(ANDROID_COARSE_LANDSCAPE);
}
static bool emulator_window_network_toggle(void) {
qemu_net_disable = !qemu_net_disable;
if (android_modem) {
amodem_set_data_registration(
android_modem,
qemu_net_disable ? A_REGISTRATION_UNREGISTERED
: A_REGISTRATION_HOME);
}
return !qemu_net_disable;
}
static void emulator_window_framebuffer_invalidate(void) {
qframebuffer_invalidate_all();
qframebuffer_check_updates();
}
static void emulator_window_keyboard_event(void* opaque, SkinKeyCode keycode, int down) {
(void)opaque;
user_event_key(keycode, down);
}
static int emulator_window_opengles_show_window(
void* window, int x, int y, int w, int h, float rotation) {
if (s_use_emugl_subwindow) {
return android_showOpenglesWindow(window, x, y, w, h, rotation);
} else {
return 0;
}
}
static int emulator_window_opengles_hide_window(void) {
if (s_use_emugl_subwindow) {
return android_hideOpenglesWindow();
} else {
return 0;
}
}
static void emulator_window_opengles_redraw_window(void) {
if (s_use_emugl_subwindow) {
android_redrawOpenglesWindow();
}
}
// Used as an emugl callback to get each frame of GPU display.
static void _emulator_window_on_gpu_frame(void* context,
int width,
int height,
const void* pixels) {
EmulatorWindow* emulator = (EmulatorWindow*)context;
// This function is called from an EmuGL thread, which cannot
// call the skin_ui_update_gpu_frame() function. Create a GpuFrame
// instance, and send its address into the pipe.
skin_ui_update_gpu_frame(emulator->ui, width, height, pixels);
}
static void
emulator_window_setup( EmulatorWindow* emulator )
{
static const SkinWindowFuncs my_window_funcs = {
.key_event = &emulator_window_window_key_event,
.mouse_event = &emulator_window_window_mouse_event,
.generic_event = &emulator_window_window_generic_event,
.opengles_show = &emulator_window_opengles_show_window,
.opengles_hide = &emulator_window_opengles_hide_window,
.opengles_redraw = &emulator_window_opengles_redraw_window,
.opengles_free = &android_stopOpenglesRenderer,
};
static const SkinTrackBallParameters my_trackball_params = {
.diameter = 30,
.ring = 2,
.ball_color = 0xffe0e0e0,
.dot_color = 0xff202020,
.ring_color = 0xff000000,
.event_func = &emulator_window_trackball_event,
};
if (emulator->opts->no_window || emulator->ui) {
return;
}
SkinUIParams my_ui_params = {
.enable_touch = !androidHwConfig_isScreenNoTouch(android_hw),
.enable_dpad = android_hw->hw_dPad != 0,
.enable_keyboard = android_hw->hw_keyboard != 0,
.enable_trackball = android_hw->hw_trackBall != 0,
.window_x = emulator->win_x,
.window_y = emulator->win_y,
.window_scale = get_default_scale(emulator->opts),
.keyboard_charmap = emulator->opts->charmap,
.keyboard_raw_keys = emulator->opts->raw_keys != 0,
#if CONFIG_QT
.win32_ignore_events = true,
#else
.win32_ignore_events = false,
#endif
};
write_window_name(my_ui_params.window_name,
sizeof(my_ui_params.window_name),
android_base_port,
avdInfo_getName(android_avdInfo));
static const SkinUIFuncs my_ui_funcs = {
.window_funcs = &my_window_funcs,
.trackball_params = &my_trackball_params,
.keyboard_event = &emulator_window_keyboard_event, //user_event_key,
.keyboard_flush = &user_event_keycodes,
.network_toggle = &emulator_window_network_toggle,
.framebuffer_invalidate = &emulator_window_framebuffer_invalidate,
};
// Determine whether to use an EmuGL sub-window or not.
const char* env = getenv("ANDROID_GL_SOFTWARE_RENDERER");
s_use_emugl_subwindow = !env || !env[0] || env[0] == '0';
if (s_use_emugl_subwindow) {
VERBOSE_PRINT(gles, "Using EmuGL sub-window for GPU display");
} else {
VERBOSE_PRINT(gles, "Using glReadPixels() for GPU display");
}
emulator->ui = skin_ui_create(emulator->layout_file,
android_hw->hw_initialOrientation,
&my_ui_funcs,
&my_ui_params);
if (!emulator->ui) {
return;
}
if (emulator->onion) {
skin_ui_set_onion(emulator->ui,
emulator->onion,
emulator->onion_rotation,
emulator->onion_alpha);
}
// Determine whether to use an EmuGL sub-window or not.
if (!s_use_emugl_subwindow) {
gpu_frame_set_post_callback(looper_newCore(),
emulator,
_emulator_window_on_gpu_frame);
}
skin_ui_reset_title(emulator->ui);
}
static void
emulator_window_fb_update( void* _emulator, int x, int y, int w, int h )
{
EmulatorWindow* emulator = _emulator;
if (emulator->opts->no_window) {
return;
}
if (!emulator->ui) {
emulator_window_setup(emulator);
}
skin_ui_update_display(emulator->ui, x, y, w, h);
}
static void
emulator_window_fb_rotate( void* _emulator, int rotation )
{
EmulatorWindow* emulator = _emulator;
emulator_window_setup( emulator );
}
static void
emulator_window_fb_poll( void* _emulator )
{
EmulatorWindow* emulator = _emulator;
emulator_window_refresh(emulator);
}
EmulatorWindow*
emulator_window_get(void)
{
return qemulator;
}
static void emulator_window_framebuffer_free(void* opaque) {
QFrameBuffer* fb = opaque;
qframebuffer_done(fb);
free(fb);
}
static void* emulator_window_framebuffer_create(int width, int height, int bpp) {
QFrameBuffer* fb = calloc(1, sizeof(*fb));
qframebuffer_init(fb, width, height, 0,
bpp == 32 ? QFRAME_BUFFER_RGBX_8888
: QFRAME_BUFFER_RGB565 );
qframebuffer_fifo_add(fb);
return fb;
}
static void* emulator_window_framebuffer_get_pixels(void* opaque) {
QFrameBuffer* fb = opaque;
return fb->pixels;
}
static int emulator_window_framebuffer_get_depth(void* opaque) {
QFrameBuffer* fb = opaque;
return fb->bits_per_pixel;
}
int
emulator_window_init( EmulatorWindow* emulator,
AConfig* aconfig,
const char* basepath,
int x,
int y,
AndroidOptions* opts )
{
static const SkinFramebufferFuncs skin_fb_funcs = {
.create_framebuffer = &emulator_window_framebuffer_create,
.free_framebuffer = &emulator_window_framebuffer_free,
.get_pixels = &emulator_window_framebuffer_get_pixels,
.get_depth = &emulator_window_framebuffer_get_depth,
};
emulator->aconfig = aconfig;
emulator->layout_file =
skin_file_create_from_aconfig(aconfig,
basepath,
&skin_fb_funcs);
emulator->ui = NULL;
emulator->win_x = x;
emulator->win_y = y;
emulator->opts[0] = opts[0];
/* register as a framebuffer clients for all displays defined in the skin file */
SKIN_FILE_LOOP_PARTS( emulator->layout_file, part )
SkinDisplay* disp = part->display;
if (disp->valid) {
qframebuffer_add_client( disp->framebuffer,
emulator,
emulator_window_fb_update,
emulator_window_fb_rotate,
emulator_window_fb_poll,
NULL );
}
SKIN_FILE_LOOP_END_PARTS
/* initialize hardware control support */
AndroidHwControlFuncs funcs;
funcs.light_brightness = emulator_window_light_brightness;
android_hw_control_set(emulator, &funcs);
return 0;
}
void
emulator_window_done(EmulatorWindow* emulator)
{
if (emulator->ui) {
skin_ui_free(emulator->ui);
emulator->ui = NULL;
}
if (emulator->layout_file) {
skin_file_free(emulator->layout_file);
emulator->layout_file = NULL;
}
}
QFrameBuffer*
emulator_window_get_first_framebuffer(EmulatorWindow* emulator)
{
/* register as a framebuffer clients for all displays defined in the skin file */
SKIN_FILE_LOOP_PARTS( emulator->layout_file, part )
SkinDisplay* disp = part->display;
if (disp->valid) {
return disp->framebuffer;
}
SKIN_FILE_LOOP_END_PARTS
return NULL;
}
/*
* Helper routines
*/
static int
get_device_dpi( AndroidOptions* opts )
{
int dpi_device = android_hw->hw_lcd_density;
if (opts->dpi_device != NULL) {
char* end;
dpi_device = strtol( opts->dpi_device, &end, 0 );
if (end == NULL || *end != 0 || dpi_device <= 0) {
fprintf(stderr, "argument for -dpi-device must be a positive integer. Aborting\n" );
exit(1);
}
}
return dpi_device;
}
static double
get_default_scale( AndroidOptions* opts )
{
int dpi_device = get_device_dpi( opts );
int dpi_monitor = -1;
double scale = 0.0;
/* possible values for the 'scale' option are
* 'auto' : try to determine the scale automatically
* '<number>dpi' : indicates the host monitor dpi, compute scale accordingly
* '<fraction>' : use direct scale coefficient
*/
if (opts->scale) {
if (!strcmp(opts->scale, "auto"))
{
/* we need to get the host dpi resolution ? */
int xdpi, ydpi;
if (skin_winsys_get_monitor_dpi(&xdpi, &ydpi) < 0) {
fprintf(stderr, "could not get monitor DPI resolution from system. please use -dpi-monitor to specify one\n" );
exit(1);
}
D( "system reported monitor resolutions: xdpi=%d ydpi=%d\n", xdpi, ydpi);
dpi_monitor = (xdpi + ydpi+1)/2;
}
else
{
char* end;
scale = strtod( opts->scale, &end );
if (end && end[0] == 'd' && end[1] == 'p' && end[2] == 'i' && end[3] == 0) {
if ( scale < 20 || scale > 1000 ) {
fprintf(stderr, "emulator: ignoring bad -scale argument '%s': %s\n", opts->scale,
"host dpi number must be between 20 and 1000" );
exit(1);
}
dpi_monitor = scale;
scale = 0.0;
}
else if (end == NULL || *end != 0) {
fprintf(stderr, "emulator: ignoring bad -scale argument '%s': %s\n", opts->scale,
"not a number or the 'auto' keyword" );
exit(1);
}
else if ( scale < 0.1 || scale > 3. ) {
fprintf(stderr, "emulator: ignoring bad -window-scale argument '%s': %s\n", opts->scale,
"must be between 0.1 and 3.0" );
exit(1);
}
}
}
if (scale == 0.0 && dpi_monitor > 0)
scale = dpi_monitor*1.0/dpi_device;
return scale;
}
/* called periodically to poll for user input events */
static void emulator_window_refresh(EmulatorWindow* emulator)
{
/* this will eventually call sdl_update if the content of the VGA framebuffer
* has changed */
qframebuffer_check_updates();
if (emulator->ui) {
if (skin_ui_process_events(emulator->ui)) {
// Quit program.
skin_ui_free(emulator->ui);
emulator->ui = NULL;
qemu_system_shutdown_request();
}
}
}
/*
* android/console.c helper routines.
*/
void
android_emulator_set_window_scale(double scale, int is_dpi)
{
EmulatorWindow* emulator = qemulator;
if (is_dpi)
scale /= get_device_dpi( emulator->opts );
if (emulator->ui) {
skin_ui_set_scale(emulator->ui, scale);
}
}
void
android_emulator_set_base_port( int port )
{
if (qemulator->ui) {
/* Base port is already set in the emulator's core. */
char buff[32];
write_window_name(buff,
sizeof(buff),
android_base_port,
avdInfo_getName(android_avdInfo));
skin_ui_set_name(qemulator->ui, buff);
}
}
SkinLayout*
emulator_window_get_layout(EmulatorWindow* emulator)
{
if (emulator->ui) {
return skin_ui_get_current_layout(emulator->ui);
} else {
return emulator->layout_file->layouts;
}
}