blob: 0f2a055d9a2529359242778ca8d6f5914f934967 [file] [log] [blame] [edit]
/* 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"
extern "C" {
#include "qemu/osdep.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 void android_display_switch(DisplayChangeListener* dcl,
DisplaySurface* new_surface_unused) {
if (QFrameBuffer* qfbuff = asDcl(dcl)->fb) {
qframebuffer_rotate(qfbuff, 0);
}
}
static void android_display_refresh(DisplayChangeListener* dcl) {
if (QFrameBuffer* qfbuff = asDcl(dcl)->fb) {
qframebuffer_poll(qfbuff);
}
}
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 DisplayChangeListenerOps dclOps = {};
bool android_display_init(DisplayState* ds, QFrameBuffer* qf) {
QemuConsole* const con = find_graphic_console();
if (!con) {
return false;
}
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;
dcl->ops = &dclOps;
register_displaychangelistener(dcl);
return true;
}
extern "C" void sdl_display_early_init(int opengl) {
(void)opengl;
}
extern "C" bool sdl_display_init(DisplayState* ds,
int full_screen,
int no_frame) {
(void)full_screen;
(void)no_frame;
EmulatorWindow* const emulator = emulator_window_get();
if (emulator->opts->no_window) {
return true;
}
return android_display_init(ds, qframebuffer_fifo_get());
}