diff mbox

Add QEMU DirectFB display driver

Message ID 1273856330-15161-1-git-send-email-julian.pidancet@citrix.com
State New
Headers show

Commit Message

Julian Pidancet May 14, 2010, 4:58 p.m. UTC
This patch implements a DirectFB driver for QEMU. It allows Qemu to
draw a VM graphic output directly in the framebuffer of the host,
without having to rely on X11.
DirectFB also provides with a generic interface take advantage of graphic
hardware acceleration for a bunch of different supported cards.

In this driver, the DirectFB library gives Qemu a pointer to mapped
video memory, which allows Qemu to update the display without extra copy.
In the case where the guest framebuffer is not compatible with the host
framebuffer, DirectFB surface blitting functions are used and can be
accellerated wherever it is possible with the hardware.

DirectFB is a thin library heavily used in embedded or minimal systems
which don't require X11 overhead. One use case would be to build a
Xen-based client-class hypervisor, with a minimal dom0 running Qemu as
device-model. The dom0 could render the domU graphical outputs on the
physical screen using this patch without having X11 installed.

The other solution would be to use the DirectFB driver for SDL which
would allow to do slightly the same as this patch. But that would mean
having to deal with an additional layer in the graphical stack, which is
not exactly what one wants from a performance or a complexity point of
view.
As an example, the SDL library gives no garantee that the surface
pointer returned by SDL_SetVideoMode(), if called with the
SDL_HWSURFACE, will be located in video memory [1], especially if the
SDL main surface is not fullscreen. For this reason, you can never
assume that SDL will not perform extra copy operations on your behalf
without notifying you.

[1] http://www.libsdl.org/cgi/docwiki.cgi/SDL_SetVideoMode

Signed-off-by: Julian Pidancet <julian.pidancet@citrix.com>
---
 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

Comments

Anthony Liguori May 14, 2010, 5:07 p.m. UTC | #1
On 05/14/2010 11:58 AM, Julian Pidancet wrote:
> This patch implements a DirectFB driver for QEMU. It allows Qemu to
> draw a VM graphic output directly in the framebuffer of the host,
> without having to rely on X11.
> DirectFB also provides with a generic interface take advantage of graphic
> hardware acceleration for a bunch of different supported cards.
>
> In this driver, the DirectFB library gives Qemu a pointer to mapped
> video memory, which allows Qemu to update the display without extra copy.
> In the case where the guest framebuffer is not compatible with the host
> framebuffer, DirectFB surface blitting functions are used and can be
> accellerated wherever it is possible with the hardware.
>
> DirectFB is a thin library heavily used in embedded or minimal systems
> which don't require X11 overhead. One use case would be to build a
> Xen-based client-class hypervisor, with a minimal dom0 running Qemu as
> device-model. The dom0 could render the domU graphical outputs on the
> physical screen using this patch without having X11 installed.
>
> The other solution would be to use the DirectFB driver for SDL which
> would allow to do slightly the same as this patch. But that would mean
> having to deal with an additional layer in the graphical stack, which is
> not exactly what one wants from a performance or a complexity point of
> view.
> As an example, the SDL library gives no garantee that the surface
> pointer returned by SDL_SetVideoMode(), if called with the
> SDL_HWSURFACE, will be located in video memory [1], especially if the
> SDL main surface is not fullscreen. For this reason, you can never
> assume that SDL will not perform extra copy operations on your behalf
> without notifying you.
>    

But that's a feature.  One would assume that SDL tries to allocate a 
hardware surface whenever it can.

Can you provide some performance data to justify this since SDL provides 
the same ability?

Regards,

Anthony Liguori

> [1] http://www.libsdl.org/cgi/docwiki.cgi/SDL_SetVideoMode
>
> Signed-off-by: Julian Pidancet<julian.pidancet@citrix.com>
> ---
>   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,&region, 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
> +@item -directfb
> +@findex -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);
>
Gerd Hoffmann May 17, 2010, 10:53 a.m. UTC | #2
> +directfb="no"

Should be ="" (aka autodetect).

> +if test "$directfb" = "yes" ; then
> +  directfb_libs=`directfb-config --libs`
> +  directfb_cflags=`directfb-config --cflags`
> +  libs_softmmu="$directfb_libs $libs_softmmu"
> +fi

use pkgconfig here.  directfb-config most likely is just a pkgconfig 
wrapper anyway.

cheeers,
   Gerd
Gerd Hoffmann May 17, 2010, 10:58 a.m. UTC | #3
Hi,

> Can you provide some performance data to justify this since SDL provides
> the same ability?

IMHO no performance data is needed to justify this because SDL running 
on top of the linux framebuffer is simply unusable IMHO.

cheers,
   Gerd
Christoph Hellwig May 17, 2010, 11:44 a.m. UTC | #4
On Fri, May 14, 2010 at 05:58:50PM +0100, Julian Pidancet wrote:
> This patch implements a DirectFB driver for QEMU. It allows Qemu to
> draw a VM graphic output directly in the framebuffer of the host,
> without having to rely on X11.
> DirectFB also provides with a generic interface take advantage of graphic
> hardware acceleration for a bunch of different supported cards.

Doesn't DirectFB still require utterly broken out of tree kernel
patches?
Julian Pidancet May 17, 2010, 12:04 p.m. UTC | #5
On 05/17/2010 11:53 AM, Gerd Hoffmann wrote:
>> +directfb="no"
> 
> Should be ="" (aka autodetect).
>



>> +if test "$directfb" = "yes" ; then
>> +  directfb_libs=`directfb-config --libs`
>> +  directfb_cflags=`directfb-config --cflags`
>> +  libs_softmmu="$directfb_libs $libs_softmmu"
>> +fi
> 
> use pkgconfig here.  directfb-config most likely is just a pkgconfig 
> wrapper anyway.
> 

Unfortunately, directfb-config is not a pkgconfig wrapper, it is a standalone shell script.
Thus, I don't know if there is an easier way of "autodetecting" directfb than probing the directfb-config script.
Julian Pidancet May 17, 2010, 12:14 p.m. UTC | #6
On 05/17/2010 12:44 PM, Christoph Hellwig wrote:
> On Fri, May 14, 2010 at 05:58:50PM +0100, Julian Pidancet wrote:
>> This patch implements a DirectFB driver for QEMU. It allows Qemu to
>> draw a VM graphic output directly in the framebuffer of the host,
>> without having to rely on X11.
>> DirectFB also provides with a generic interface take advantage of graphic
>> hardware acceleration for a bunch of different supported cards.
> 
> Doesn't DirectFB still require utterly broken out of tree kernel
> patches?
> 

I have not heard about that, sorry.
My test configuration has an i915-like Intel graphic card, and a nearly standard 2.6.32 linux kernel. No specific patching was required.
Christoph Hellwig May 17, 2010, 12:35 p.m. UTC | #7
On Mon, May 17, 2010 at 01:14:05PM +0100, Julian Pidancet wrote:
> I have not heard about that, sorry.
> My test configuration has an i915-like Intel graphic card, and a nearly standard 2.6.32 linux kernel. No specific patching was required.

You're right.  I've checked it and the horrible kernel code is entirely
optional.  Sorry for the noise.
Gerd Hoffmann May 17, 2010, 7:25 p.m. UTC | #8
On 05/17/10 14:04, Julian Pidancet wrote:
> On 05/17/2010 11:53 AM, Gerd Hoffmann wrote:
>>> +directfb="no"
>>
>> Should be ="" (aka autodetect).
>>
>
>
>
>>> +if test "$directfb" = "yes" ; then
>>> +  directfb_libs=`directfb-config --libs`
>>> +  directfb_cflags=`directfb-config --cflags`
>>> +  libs_softmmu="$directfb_libs $libs_softmmu"
>>> +fi
>>
>> use pkgconfig here.  directfb-config most likely is just a pkgconfig
>> wrapper anyway.
>>
>
> Unfortunately, directfb-config is not a pkgconfig wrapper, it is a standalone shell script.
> Thus, I don't know if there is an easier way of "autodetecting" directfb than probing the directfb-config script.
>

'pkg-config directfb'
	exit code 0 => success
	exit code 1 => failure

pkg-config --cflags directfb
	finds cflags

Check 'pkg-config --help' && man page for more info.

Oh, and btw: the patch doesn't build when applied to latest master bits.

HTH,
   Gerd
diff mbox

Patch

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, &region, 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
+@item -directfb
+@findex -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);