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);
