blob: 8b1d5b831a7e715faa2d39c816655ee496095e69 [file] [log] [blame]
/* Copyright (C) 2006-2016 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/emulation/control/user_event_agent.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/network/globals.h"
#include "android/opengles.h"
#include "android/skin/keycode.h"
#include "android/skin/qt/set-ui-emu-agent.h"
#include "android/skin/winsys.h"
#include "android/ui-emu-agent.h"
#include "android/utils/debug.h"
#include "android/utils/bufprint.h"
#include "android/utils/looper.h"
#include "android/telephony/modem_driver.h"
#define D(...) do { if (VERBOSE_CHECK(init)) dprint(__VA_ARGS__); } while (0)
/* EmulatorWindow structure instance. */
static EmulatorWindow qemulator[1];
// Our very own stash of a pointer to a device that handles user events.
const QAndroidUserEventAgent* user_event_agent;
// 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, "Android Emulator - %s:%d", avd_name, base_port);
}
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_agent->sendMouseEvent(dx, dy, 1, 0);
}
static void emulator_window_window_key_event(unsigned keycode, int down) {
user_event_agent->sendKey(keycode, down);
}
static void emulator_window_keycodes_event(int* keycodes, int count) {
user_event_agent->sendKeyCodes(keycodes, count);
}
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_agent->sendMouseEvent(x, y, 0, state);
}
static void emulator_window_set_device_orientation(SkinRotation rotation) {
android_sensors_set_coarse_orientation((AndroidCoarseOrientation) rotation);
}
static bool emulator_window_network_toggle(void) {
android_net_disable = !android_net_disable;
if (android_modem) {
amodem_set_data_registration(
android_modem,
android_net_disable ? A_REGISTRATION_UNREGISTERED
: A_REGISTRATION_HOME);
}
return !android_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_agent->sendKey(keycode, down);
}
static int emulator_window_opengles_show_window(
void* window, int x, int y, int vw, int vh, int w, int h, float dpr, float rotation) {
if (s_use_emugl_subwindow) {
return android_showOpenglesWindow(window, x, y, vw, vh, w, h, dpr, 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_set_translation(float dx, float dy) {
if (s_use_emugl_subwindow) {
android_setOpenglesTranslation(dx, dy);
}
}
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 )
{
user_event_agent = emulator->uiEmuAgent->userEvents;
static const SkinWindowFuncs my_window_funcs = {
.key_event = &emulator_window_window_key_event,
.mouse_event = &emulator_window_window_mouse_event,
.set_device_orientation = &emulator_window_set_device_orientation,
.opengles_show = &emulator_window_opengles_show_window,
.opengles_hide = &emulator_window_opengles_hide_window,
.opengles_setTranslation = &emulator_window_opengles_set_translation,
.opengles_redraw = &emulator_window_opengles_redraw_window,
};
static const SkinTrackBallParameters my_trackball_params = {
.diameter = 60,
.ring = 4,
.ball_color = 0xffe0e0e0,
.dot_color = 0xff202020,
.ring_color = 0xff000000,
.event_func = &emulator_window_trackball_event,
};
if (emulator->opts->no_window || emulator->ui) {
return;
}
if (emulator->opts->scale) {
dwarning("The -scale flag is obsolete and will be ignored.");
}
if (emulator->opts->dpi_device) {
dwarning("The -dpi-device flag is obsolete and will be ignored.");
}
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,
.enable_scale = !emulator->opts->fixed_scale,
.window_x = emulator->win_x,
.window_y = emulator->win_y,
.keyboard_charmap = emulator->opts->charmap
};
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,
.keyboard_flush = &emulator_window_keycodes_event,
.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';
// for gpu off or gpu guest, we don't use the subwindow
if (!android_hw->hw_gpu_enabled || !strcmp(android_hw->hw_gpu_mode, "guest")) {
s_use_emugl_subwindow = 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, s_use_emugl_subwindow);
if (!emulator->ui) {
return;
}
if (emulator->onion) {
skin_ui_set_onion(emulator->ui,
emulator->onion,
emulator->onion_rotation,
emulator->onion_alpha);
}
setUiEmuAgent(emulator->uiEmuAgent);
// Determine whether to use an EmuGL sub-window or not.
if (!s_use_emugl_subwindow) {
gpu_frame_set_post_callback(looper_getForThread(),
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);
}
if (!s_use_emugl_subwindow) {
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,
const AConfig* aconfig,
const char* basepath,
int x,
int y,
const AndroidOptions* opts,
const UiEmuAgent* uiEmuAgent) {
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;
// if not building for a gui-less window, create a skin layout file,
// else skip as no skin will be displayed
if (!opts->no_window) {
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) = *opts;
*(emulator->uiEmuAgent) = *uiEmuAgent;
/* register as a framebuffer clients for all displays defined in the skin file */
if (emulator->layout_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;
}
}
/* 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();
}
}
}
void
android_emulator_set_base_port( int port )
{
if (qemulator->ui) {
/* Base port is already set in the emulator's core. */
char buff[256];
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 {
if(emulator->opts->no_window) {
// in no-window mode there is no skin layout
return NULL;
} else {
return emulator->layout_file->layouts;
}
}
return NULL;
}