blob: 932fca67d3663ee928c9eb7228cbb3c566239263 [file] [log] [blame]
/* Copyright (C) 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.
*/
/* Initialization of the Android-specific DisplayState.
* Read docs/DISPLAY-STATE.TXT to understand what this
* is supposed to do.
*/
#include "android-qemu2-glue/display.h"
#include "android/emulator-window.h"
#include "android/globals.h"
extern "C" {
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "sysemu/sysemu.h"
#include "ui/console.h"
}
namespace {
struct DCLExtra : public DisplayChangeListener {
DCLExtra() { memset(this, 0, sizeof(*this)); }
QFrameBuffer* fb;
};
}
static DCLExtra* asDcl(DisplayChangeListener* dcl) {
return static_cast<DCLExtra*>(dcl);
}
/*
TECHNICAL NOTE:
DisplayState <--> QFrameBuffer <--> QEmulator/SDL
*/
/* QFrameBuffer producer callbacks */
/* this is called periodically by the GUI timer to check for updates
* and poll user events. Use vga_hw_update().
*/
static void android_display_producer_check(void* opaque) {
/* core: call vga_hw_update(). this will eventually
* lead to calls to android_display_update()
*/
(void)opaque;
graphic_hw_update(NULL);
}
static void android_display_producer_invalidate(void* opaque) {
(void)opaque;
graphic_hw_invalidate(NULL);
}
static void android_display_producer_detach(void* opaque) {
// the framebuffer is being deleted, clean it up in the DCL as well
if (const auto dcl = static_cast<DCLExtra*>(opaque)) {
dcl->fb = nullptr;
}
}
/* QFrameBuffer client callbacks */
/* this is called from dpy_update() each time a hardware framebuffer
* rectangular update was detected. Send this to the QFrameBuffer.
*/
static void android_display_update(DisplayChangeListener* dcl,
int x,
int y,
int w,
int h) {
if (QFrameBuffer* qfbuff = asDcl(dcl)->fb) {
qframebuffer_update(qfbuff, x, y, w, h);
}
}
static pixman_image_t* get_pixman_image_from_qframebuffer(QFrameBuffer* qf) {
pixman_format_code_t format =
qemu_default_pixman_format(qf->bits_per_pixel, true);
return pixman_image_create_bits(format, qf->width, qf->height,
(uint32_t*)qf->pixels, qf->pitch);
}
static void android_display_switch(DisplayChangeListener* dcl,
DisplaySurface* new_surface) {
if (QFrameBuffer* qfbuff = asDcl(dcl)->fb) {
if (android_hw->hw_arc) {
static pixman_image_t * qfb_image =
get_pixman_image_from_qframebuffer(qfbuff);
pixman_image_composite(PIXMAN_OP_OVER,
new_surface->image, NULL,
qfb_image,
0, 0, 0, 0, 0, 0,
qfbuff->width, qfbuff->height);
}
qframebuffer_rotate(qfbuff, 0);
}
}
static void android_display_refresh(DisplayChangeListener* dcl) {
if (QFrameBuffer* qfbuff = asDcl(dcl)->fb) {
qframebuffer_poll(qfbuff);
}
}
extern void * android_gl_create_context(DisplayChangeListener*, QEMUGLParams *);
extern void android_gl_destroy_context(DisplayChangeListener*, void *);
extern int android_gl_make_context_current(DisplayChangeListener*, void *);
extern void android_gl_scanout_texture(DisplayChangeListener*, uint32_t, bool,
uint32_t, uint32_t, uint32_t, uint32_t,
uint32_t, uint32_t);
extern void android_gl_scanout_flush(DisplayChangeListener *, uint32_t,
uint32_t, uint32_t, uint32_t);
static void android_gl_scanout_disable(DisplayChangeListener *dcl) {
fprintf(stderr, "stub %s\n", __func__);
}
static QemuConsole* find_graphic_console() {
// find the first graphic console (Android emulator has only one usually)
for (int i = 0;; i++) {
QemuConsole* const c = qemu_console_lookup_by_index(i);
if (!c) {
break;
}
if (qemu_console_is_graphic(c)) {
return c;
}
}
return NULL;
}
static int last_graphic_console_index() {
int last = -1;
for (int i = 0;; i++) {
QemuConsole* const c = qemu_console_lookup_by_index(i);
if (!c) {
break;
}
if (qemu_console_is_graphic(c)) {
last = i;
}
}
return last;
}
static DisplayChangeListenerOps dclOps = {};
void android_display_init_no_window(QFrameBuffer* qf) {
// Only need to attach the check and invalidate callbacks in no-window mode
// to request for a refresh of the framebuffer.
qframebuffer_set_producer(qf, nullptr, android_display_producer_check,
android_display_producer_invalidate, nullptr);
}
bool android_display_init(DisplayState* ds, QFrameBuffer* qf) {
QemuConsole* con = find_graphic_console();
if (!con) {
return false;
}
if (android_hw->hw_arc) {
/* We don't use goldfish_fb in cros now. so
* just pick up last graphic console */
int index = last_graphic_console_index();
if (index < 0) {
return false;
}
console_select(index);
con = qemu_console_lookup_by_index(index);
QemuUIInfo info = {
0, 0,
(uint32_t)qf->width,
(uint32_t)qf->height,
};
dpy_set_ui_info(con, &info);
}
const auto dcl = new DCLExtra();
qframebuffer_set_producer(qf, dcl,
android_display_producer_check,
android_display_producer_invalidate,
android_display_producer_detach);
/* Replace the display surface with one with the right dimensions */
pixman_format_code_t format =
qemu_default_pixman_format(qf->bits_per_pixel, true);
auto surface = qemu_create_displaysurface_from(
qf->width, qf->height, format, qf->pitch, (uint8_t*)qf->pixels);
dpy_gfx_replace_surface(con, surface);
/* Register a change listener for it */
dcl->fb = qf;
dclOps.dpy_name = "qemu2-glue";
dclOps.dpy_refresh = &android_display_refresh;
dclOps.dpy_gfx_update = &android_display_update;
dclOps.dpy_gfx_switch = &android_display_switch;
if (android_hw->hw_arc) {
dclOps.dpy_gl_ctx_create = &android_gl_create_context;
dclOps.dpy_gl_ctx_destroy = &android_gl_destroy_context;
dclOps.dpy_gl_ctx_make_current = &android_gl_make_context_current;
dclOps.dpy_gl_scanout_disable = &android_gl_scanout_disable;
dclOps.dpy_gl_scanout_texture = &android_gl_scanout_texture;
dclOps.dpy_gl_update = &android_gl_scanout_flush;
dcl->con = con;
}
dcl->ops = &dclOps;
register_displaychangelistener(dcl);
return true;
}
extern "C" void sdl_display_early_init(DisplayOptions* opts) {
if (opts->gl && android_hw->hw_arc) {
display_opengl = 1;
}
}
extern "C" int sdl_display_init(DisplayState* ds, DisplayOptions* opts) {
(void)opts;
EmulatorWindow* const emulator = emulator_window_get();
if (emulator->opts->no_window) {
return true;
}
return android_display_init(ds, qframebuffer_fifo_get());
}
static QemuDisplay qemu_display_android = {
.type = DISPLAY_TYPE_SDL,
.early_init = sdl_display_early_init,
.init = sdl_display_init,
};
static void register_android_ui(void)
{
qemu_display_register(&qemu_display_android);
}
type_init(register_android_ui);