| /* 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; |
| } |