[1] http://www.libsdl.org/cgi/docwiki.cgi/SDL_SetVideoMode
Signed-off-by: Julian Pidancet<address@hidden>
---
Makefile | 4 +
Makefile.objs | 1 +
configure | 21 +++
console.h | 3 +
directfb.c | 394 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
qemu-options.hx | 10 ++
sysemu.h | 1 +
vl.c | 12 ++
8 files changed, 446 insertions(+), 0 deletions(-)
create mode 100644 directfb.c
diff --git a/Makefile b/Makefile
index eb9e02b..6932c81 100644
--- a/Makefile
+++ b/Makefile
@@ -106,6 +106,10 @@ sdl.o: sdl.c keymaps.h sdl_keysym.h sdl_zoom.h
sdl.o audio/sdlaudio.o sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
+directfb.o: directfb.c
+
+directfb.o: QEMU_CFLAGS += $(DIRECTFB_CFLAGS)
+
acl.o: acl.h acl.c
vnc.h: vnc-tls.h vnc-auth-vencrypt.h vnc-auth-sasl.h keymaps.h
diff --git a/Makefile.objs b/Makefile.objs
index ecdd53e..0904b07 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -102,6 +102,7 @@ common-obj-y += $(addprefix audio/, $(audio-obj-y))
common-obj-y += keymaps.o
common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
common-obj-$(CONFIG_CURSES) += curses.o
+common-obj-$(CONFIG_DIRECTFB) += directfb.o
common-obj-y += vnc.o acl.o d3des.o
common-obj-y += vnc-encoding-zlib.o vnc-encoding-hextile.o
common-obj-y += iov.o
diff --git a/configure b/configure
index 36d028f..eb73415 100755
--- a/configure
+++ b/configure
@@ -258,6 +258,7 @@ kvm=""
kvm_para=""
nptl=""
sdl=""
+directfb="no"
sparse="no"
uuid=""
vde=""
@@ -502,6 +503,10 @@ for opt do
;;
--sysconfdir=*) sysconfdir="$optarg"
;;
+ --disable-directfb) directfb="no"
+ ;;
+ --enable-directfb) directfb="yes"
+ ;;
--disable-sdl) sdl="no"
;;
--enable-sdl) sdl="yes"
@@ -763,6 +768,8 @@ echo " --disable-strip disable stripping binaries"
echo " --disable-werror disable compilation abort on warning"
echo " --disable-sdl disable SDL"
echo " --enable-sdl enable SDL"
+echo " --disable-directfb disable DirectFB"
+echo " --enable-directfb enable DirectFB"
echo " --enable-cocoa enable COCOA (Mac OS X only)"
echo " --audio-drv-list=LIST set audio drivers list:"
echo " Available drivers: $audio_possible_drivers"
@@ -1062,6 +1069,15 @@ if test "$sparse" != "no" ; then
fi
##########################################
+# DirectFB probe
+
+if test "$directfb" = "yes" ; then
+ directfb_libs=`directfb-config --libs`
+ directfb_cflags=`directfb-config --cflags`
+ libs_softmmu="$directfb_libs $libs_softmmu"
+fi
+
+##########################################
# SDL probe
if $pkgconfig sdl --modversion>/dev/null 2>&1; then
@@ -1999,6 +2015,7 @@ if test "$darwin" = "yes" ; then
echo "Cocoa support $cocoa"
fi
echo "SDL support $sdl"
+echo "DirectFB support $directfb"
echo "curses support $curses"
echo "curl support $curl"
echo "check support $check_utests"
@@ -2169,6 +2186,10 @@ fi
if test "$cocoa" = "yes" ; then
echo "CONFIG_COCOA=y">> $config_host_mak
fi
+if test "$directfb" = "yes" ; then
+ echo "CONFIG_DIRECTFB=y">> $config_host_mak
+ echo "DIRECTFB_CFLAGS=$directfb_cflags">> $config_host_mak
+fi
if test "$curses" = "yes" ; then
echo "CONFIG_CURSES=y">> $config_host_mak
fi
diff --git a/console.h b/console.h
index 6def115..d1dd211 100644
--- a/console.h
+++ b/console.h
@@ -335,6 +335,9 @@ void qemu_console_resize(DisplayState *ds, int width, int
height);
void qemu_console_copy(DisplayState *ds, int src_x, int src_y,
int dst_x, int dst_y, int w, int h);
+/* directfb.c */
+void directfb_display_init(DisplayState *ds);
+
/* sdl.c */
void sdl_display_init(DisplayState *ds, int full_screen, int no_frame);
diff --git a/directfb.c b/directfb.c
new file mode 100644
index 0000000..6dea99a
--- /dev/null
+++ b/directfb.c
@@ -0,0 +1,394 @@
+/*
+ * QEMU DirectFB display driver
+ *
+ * Copyright (c) 2010 Citrix Systems, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include<console.h>
+#include<keymaps.h>
+
+#include<directfb.h>
+
+static IDirectFB *dfb = NULL;
+static IDirectFBSurface *primary = NULL;
+static IDirectFBEventBuffer *events = NULL;
+static IDirectFBSurface *guest = NULL;
+
+static void *screen_data = NULL;
+static int screen_pitch = 0;
+static int screen_bpp = 0;
+static int screen_width = 0;
+static int screen_height = 0;
+static int scaling = 0;
+
+#define DIRECTFB_IS_VIDEO_PTR(p) \
+ (p>= (uint8_t *) screen_data&& \
+ p< (uint8_t *) screen_data + screen_height * screen_pitch)
+
+static DFBSurfacePixelFormat directfb_bpp_to_pixelformat(int bpp)
+{
+ switch (bpp) {
+ case 16:
+ return DSPF_RGB16;
+ case 24:
+ return DSPF_RGB24;
+ case 32:
+ return DSPF_RGB32;
+ default:
+ return DSPF_UNKNOWN;
+ }
+}
+
+static void directfb_clearscreen(void)
+{
+ if (screen_data != NULL) {
+ /* Surface is locked */
+ memset(screen_data, 0x0,
+ screen_pitch * screen_height);
+ } else {
+ primary->SetColor(primary, 0x0, 0x0, 0x0, 0x0);
+ primary->FillRectangle(primary, 0, 0, screen_width, screen_height);
+ }
+}
+
+static void directfb_update(struct DisplayState *s, int x, int y, int w, int h)
+{
+ DFBRegion region = {x, y, x + w, y + h};
+
+ if (guest) {
+ if (scaling) {
+ primary->StretchBlit(primary, guest, NULL, NULL);
+ } else {
+ int xoff = (screen_width - ds_get_width(s)) / 2;
+ int yoff = (screen_height - ds_get_height(s)) / 2;
+
+ primary->Blit(primary, guest, NULL, xoff, yoff);
+
+ region.x1 += xoff;
+ region.y1 += yoff;
+ region.x2 += xoff;
+ region.x2 += yoff;
+ }
+ }
+
+ primary->Flip(primary,®ion, DSFLIP_NONE);
+}
+
+static void directfb_setdata(DisplayState *s)
+{
+ DFBSurfaceDescription dsc;
+
+ if (guest) {
+ guest->Release(guest);
+ guest = NULL;
+ }
+
+ dsc.flags = DSDESC_WIDTH | DSDESC_HEIGHT |
+ DSDESC_PIXELFORMAT | DSDESC_PREALLOCATED;
+ dsc.width = ds_get_width(s);
+ dsc.height = ds_get_height(s);
+ dsc.pixelformat = directfb_bpp_to_pixelformat(ds_get_bits_per_pixel(s));
+ dsc.preallocated[0].data = ds_get_data(s);
+ dsc.preallocated[0].pitch = ds_get_linesize(s);
+
+ dfb->CreateSurface(dfb,&dsc,&guest);
+}
+
+static void directfb_resize(struct DisplayState *s)
+{
+ directfb_clearscreen();
+
+ if (scaling || ds_get_bits_per_pixel(s) != screen_bpp ||
+ ds_get_linesize(s) != screen_pitch ||
+ !DIRECTFB_IS_VIDEO_PTR(ds_get_data(s))) {
+
+ directfb_setdata(s);
+ } else {
+ if (guest) {
+ guest->Release(guest);
+ guest = NULL;
+ }
+ }
+}
+
+static int directfb_buttons_state(DFBInputEvent *ev)
+{
+ int buttons = 0;
+
+ if (ev->buttons& DIBM_LEFT) {
+ buttons |= MOUSE_EVENT_LBUTTON;
+ }
+ if (ev->buttons& DIBM_RIGHT) {
+ buttons |= MOUSE_EVENT_RBUTTON;
+ }
+ if (ev->buttons& DIBM_MIDDLE) {
+ buttons |= MOUSE_EVENT_MBUTTON;
+ }
+
+ return buttons;
+}
+
+static void directfb_put_keycode(char keycode, int up)
+{
+ int scancode = keycode;
+
+ /* Pause/Break */
+ if (keycode == 119) {
+ scancode = 0x45;
+ kbd_put_keycode(0xe1);
+ kbd_put_keycode(0x1d | up ? 0x80 : 0x0);
+ } else {
+ /* grey key */
+ if (keycode>= 0x60&& keycode< 0x70) {
+ const char esc[16] = {0x1c, 0x1d, 0x35, 0x37,
+ 0x38, 0x46, 0x47, 0x48,
+ 0x49, 0x4b, 0x4d, 0x4f,
+ 0x50, 0x51, 0x52, 0x53};
+ scancode = esc[keycode - 0x60];
+ kbd_put_keycode(0xe0);
+
+ /* PrintScreen */
+ if (keycode == 99) {
+ scancode = 0x37;
+ kbd_put_keycode(0x2a | up ? 0x80 : 0x0);
+ kbd_put_keycode(0xe0);
+ }
+ }
+ }
+
+ kbd_put_keycode(scancode | (up ? 0x80 : 0x0));
+}
+
+static void directfb_toggle_fullscreen(struct DisplayState *ds)
+{
+ scaling = !scaling;
+
+ vga_hw_invalidate();
+ vga_hw_update();
+}
+
+static void directfb_refresh(struct DisplayState *s)
+{
+ DFBInputEvent ev;
+
+ vga_hw_update();
+
+ while (events->GetEvent(events, DFB_EVENT(&ev)) == DFB_OK) {
+ switch (ev.type) {
+ case DIET_KEYRELEASE:
+ directfb_put_keycode(ev.key_code, 1);
+ break;
+ case DIET_KEYPRESS:
+ /* Toggle centered/fullscreen */
+ if ((ev.modifiers& DIMM_CONTROL)&&
+ (ev.modifiers& DIMM_ALT)&&
+ (ev.key_id == DIKI_ENTER)) {
+ directfb_toggle_fullscreen(s);
+ break;
+ }
+ directfb_put_keycode(ev.key_code, 0);
+ break;
+ case DIET_BUTTONPRESS:
+ case DIET_BUTTONRELEASE:
+ case DIET_AXISMOTION:
+ {
+ int buttons = directfb_buttons_state(&ev);
+ int dx = 0;
+ int dy = 0;
+ int dz = 0;
+
+ if (ev.type == DIET_AXISMOTION) {
+ if (ev.axis == DIAI_X) {
+ dx = ev.axisrel;
+ }
+ if (ev.axis == DIAI_Y) {
+ dy = ev.axisrel;
+ }
+ if (ev.axis == DIAI_Z) {
+ dz = ev.axisrel;
+ }
+ }
+
+ kbd_mouse_event(dx, dy, dz, buttons);
+ break;
+ }
+ case DIET_UNKNOWN:
+ default:
+ break;
+
+ }
+ }
+}
+
+static DisplaySurface* directfb_create_displaysurface(int width, int height)
+{
+ DisplaySurface *surface = (DisplaySurface*)
qemu_mallocz(sizeof(DisplaySurface));
+ DFBSurfacePixelFormat spf;
+ surface->width = width;
+ surface->height = height;
+
+ primary->GetPixelFormat(primary,&spf);
+
+ if (scaling) {
+ int bytes_per_pixel = DFB_BYTES_PER_PIXEL(spf);
+
+ if (bytes_per_pixel != 2&& bytes_per_pixel != 4) {
+ bytes_per_pixel = 4;
+ }
+
+ surface->pf = qemu_default_pixelformat(8 * bytes_per_pixel);
+ surface->linesize = width * bytes_per_pixel;
+
+ surface->flags = QEMU_ALLOCATED_FLAG;
+ surface->data = qemu_mallocz(surface->linesize * surface->height);
+ } else {
+ primary->Lock(primary, DSLF_READ |
DSLF_WRITE,&screen_data,&screen_pitch);
+ surface->pf = qemu_default_pixelformat(screen_bpp);
+ surface->flags = QEMU_REALPIXELS_FLAG;
+ surface->linesize = screen_pitch;
+ surface->data = screen_data +
+ ((screen_height - height) / 2) * screen_pitch +
+ ((screen_width - width) / 2) * (screen_bpp / 8);
+ }
+
+ return surface;
+}
+
+static void directfb_free_displaysurface(DisplaySurface *surface)
+{
+ if (surface == NULL)
+ return;
+
+ if (surface->flags& QEMU_ALLOCATED_FLAG) {
+ qemu_free(surface->data);
+ } else if (surface->flags& QEMU_REALPIXELS_FLAG) {
+ primary->Unlock(primary);
+ screen_data = NULL;
+ screen_pitch = 0;
+ }
+
+ surface->data = NULL;
+
+ qemu_free(surface);
+}
+
+static DisplaySurface* directfb_resize_displaysurface(DisplaySurface *surface,
+ int width,
+ int height)
+{
+ directfb_free_displaysurface(surface);
+ return directfb_create_displaysurface(width, height);
+}
+
+static DFBEnumerationResult directfb_attach_inputdevice(DFBInputDeviceID
device_id,
+
DFBInputDeviceDescription desc,
+ void *data)
+{
+ if (!strcmp(desc.vendor, "Linux")) {
+ return DFENUM_OK;
+ }
+
+ if (desc.type == DIDID_KEYBOARD || desc.type | DIDTF_MOUSE) {
+ IDirectFBInputDevice *device;
+
+ dfb->GetInputDevice(dfb, device_id,&device);
+
+ if (events == NULL) {
+ device->CreateEventBuffer(device,&events);
+ } else {
+ device->AttachEventBuffer(device, events);
+ }
+ }
+
+ return DFENUM_OK;
+}
+
+void directfb_display_init(DisplayState *ds)
+{
+ DisplayChangeListener *dcl;
+ DisplayAllocator *da;
+ DFBResult status;
+ DFBSurfaceDescription dsc;
+ DFBSurfaceCapabilities caps;
+ DFBSurfacePixelFormat spf;
+
+ /*
+ * Prevent DirectFB to read qemu command line argument in procfs and
+ * parse it.
+ */
+ char prog_name[] = "qemu";
+ char *prog_argv[] = {prog_name};
+ char **dfb_argv = prog_argv;
+ int dfb_argc = 1;
+
+ status = DirectFBInit(&dfb_argc,&dfb_argv);
+ if (status != DFB_OK) {
+ fprintf(stderr, "Could not initialize DirectFB(%d) - exiting\n",
status);
+ exit(1);
+ }
+
+ DirectFBCreate(&dfb);
+ dfb->SetCooperativeLevel(dfb, DFSCL_FULLSCREEN);
+ dsc.flags = DSDESC_CAPS;
+ dsc.caps = DSCAPS_PRIMARY | DSCAPS_VIDEOONLY | DSCAPS_SHARED;
+ status = dfb->CreateSurface(dfb,&dsc,&primary);
+
+ if (status != DFB_OK) {
+ fprintf(stderr, "Could not create DirectFB surface(%d) - exiting\n",
status);
+ exit(1);
+ }
+
+ /* Double check surface capabilities */
+ primary->GetCapabilities(primary,&caps);
+ if ((caps& dsc.caps) != dsc.caps ||
+ caps& DSCAPS_FLIPPING || caps& DSCAPS_INTERLACED ||
+ caps& DSCAPS_SYSTEMONLY) {
+ fprintf(stderr, "Wrong DirectFB surface capabilities - exiting\n");
+ exit(1);
+ }
+
+ primary->GetSize(primary,&screen_width,&screen_height);
+ primary->GetPixelFormat(primary,&spf);
+ screen_bpp = DFB_BITS_PER_PIXEL(spf);
+
+ dfb->EnumInputDevices(dfb, directfb_attach_inputdevice, NULL);
+
+ fprintf(stderr, "Initialized QEMU DirectFB driver. (%dx%d)\n",
+ screen_width, screen_height);
+
+ dcl = qemu_mallocz(sizeof(DisplayChangeListener));
+ dcl->dpy_update = directfb_update;
+ dcl->dpy_resize = directfb_resize;
+ dcl->dpy_refresh = directfb_refresh;
+ dcl->dpy_setdata = directfb_setdata;
+ register_displaychangelistener(ds, dcl);
+
+ da = qemu_mallocz(sizeof(DisplayAllocator));
+ da->create_displaysurface = directfb_create_displaysurface;
+ da->resize_displaysurface = directfb_resize_displaysurface;
+ da->free_displaysurface = directfb_free_displaysurface;
+
+ directfb_clearscreen();
+
+ if (register_displayallocator(ds, da) == da) {
+ dpy_resize(ds);
+ }
+}
diff --git a/qemu-options.hx b/qemu-options.hx
index 12f6b51..a4bdfbe 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -608,6 +608,16 @@ QEMU can display the VGA output when in text mode using a
curses/ncurses interface. Nothing is displayed in graphical mode.
ETEXI
+#ifdef CONFIG_DIRECTFB
+DEF("directfb", 0, QEMU_OPTION_directfb,
+ "-directfb enable DirectFB\n")
+#endif
+STEXI
address@hidden -directfb
address@hidden -directfb
+Enable DirectFB.
+ETEXI
+
#ifdef CONFIG_SDL
DEF("no-frame", 0, QEMU_OPTION_no_frame,
"-no-frame open SDL window without a frame and window
decorations\n",
diff --git a/sysemu.h b/sysemu.h
index fa921df..a2cd5b0 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -98,6 +98,7 @@ typedef enum DisplayType
DT_CURSES,
DT_SDL,
DT_VNC,
+ DT_DIRECTFB,
DT_NOGRAPHIC,
} DisplayType;
diff --git a/vl.c b/vl.c
index 85bcc84..e6235fa 100644
--- a/vl.c
+++ b/vl.c
@@ -3199,6 +3199,11 @@ int main(int argc, char **argv, char **envp)
case QEMU_OPTION_full_screen:
full_screen = 1;
break;
+#ifdef CONFIG_DIRECTFB
+ case QEMU_OPTION_directfb:
+ display_type = DT_DIRECTFB;
+ break;
+#endif
#ifdef CONFIG_SDL
case QEMU_OPTION_no_frame:
no_frame = 1;
@@ -3765,6 +3770,8 @@ int main(int argc, char **argv, char **envp)
if (display_type == DT_DEFAULT) {
#if defined(CONFIG_SDL) || defined(CONFIG_COCOA)
display_type = DT_SDL;
+#elif defined(CONFIG_DIRECTFB)
+ display_type = DT_DIRECTFB;
#else
display_type = DT_VNC;
vnc_display = "localhost:0,to=99";
@@ -3781,6 +3788,11 @@ int main(int argc, char **argv, char **envp)
curses_display_init(ds, full_screen);
break;
#endif
+#if defined(CONFIG_DIRECTFB)
+ case DT_DIRECTFB:
+ directfb_display_init(ds);
+ break;
+#endif
#if defined(CONFIG_SDL)
case DT_SDL:
sdl_display_init(ds, full_screen, no_frame);