diff mbox

[FYI,8/8] ui: Haiku frontend

Message ID 1284936650-1203-9-git-send-email-andreas.faerber@web.de
State New
Headers show

Commit Message

Andreas Färber Sept. 19, 2010, 10:50 p.m. UTC
Since Haiku is still in development and raw builds don't contain the
SDL or a VNC client, a native frontend is convenient. mmlr had maintained
a basic one for BeOS/Haiku until the kqemu drop. The code has been ported
to HEAD but is not yet fully updated to match CODING_STYLE.

Cc: Michael Lotz <mmlr@mlotz.ch>

Haiku's native APIs are C++ based, so we need a new make rule.
Some enabled GCC warnings are not applicable to C++ code and cause warnings.

Cc: Blue Swirl <blauwirbel@gmail.com>

Note that this frontend is another candidate user of a central qemu_main()
prototype, like Cocoa. It would also be candidate for a generic argv check
for non-graphic mode, to be shared with Cocoa.
---
 Makefile      |    2 +
 Makefile.objs |    1 +
 configure     |    1 +
 console.h     |    3 +
 rules.mak     |    6 +-
 ui/haiku.cpp  |  607 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 ui/haiku.h    |   96 +++++++++
 vl.c          |   10 +-
 8 files changed, 724 insertions(+), 2 deletions(-)
 create mode 100644 ui/haiku.cpp
 create mode 100644 ui/haiku.h
diff mbox

Patch

diff --git a/Makefile b/Makefile
index ab91d42..64eea3e 100644
--- a/Makefile
+++ b/Makefile
@@ -98,6 +98,8 @@  QEMU_CFLAGS+=$(CURL_CFLAGS)
 
 ui/cocoa.o: ui/cocoa.m
 
+ui/haiku.o: ui/haiku.cpp
+
 ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
 
 ui/vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
diff --git a/Makefile.objs b/Makefile.objs
index f702ad4..6ae0e14 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -112,6 +112,7 @@  ui-obj-y += vnc-enc-tight.o vnc-palette.o
 ui-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
 ui-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
 ui-obj-$(CONFIG_COCOA) += cocoa.o
+ui-obj-$(CONFIG_HAIKU) += haiku.o
 ifdef CONFIG_VNC_THREAD
 ui-obj-y += vnc-jobs-async.o
 else
diff --git a/configure b/configure
index 3a0d50e..b533990 100755
--- a/configure
+++ b/configure
@@ -454,6 +454,7 @@  Haiku)
   haiku="yes"
   QEMU_CFLAGS="-DB_USE_POSITIVE_POSIX_ERRORS $QEMU_CFLAGS"
   LIBS="-lposix_error_mapper -lnetwork $LIBS"
+  LIBS="-lstdc++ -lbe -lgame $LIBS"
 ;;
 *)
   audio_drv_list="oss"
diff --git a/console.h b/console.h
index aafb031..9a8268a 100644
--- a/console.h
+++ b/console.h
@@ -364,6 +364,9 @@  void sdl_display_init(DisplayState *ds, int full_screen, int no_frame);
 /* cocoa.m */
 void cocoa_display_init(DisplayState *ds, int full_screen);
 
+/* haiku.cpp */
+void haiku_display_init(DisplayState *ds, int full_screen);
+
 /* vnc.c */
 void vnc_display_init(DisplayState *ds);
 void vnc_display_close(DisplayState *ds);
diff --git a/rules.mak b/rules.mak
index c843a13..29481b9 100644
--- a/rules.mak
+++ b/rules.mak
@@ -8,6 +8,7 @@  MAKEFLAGS += -rR
 %.d:
 %.h:
 %.c:
+%.cpp:
 %.m:
 %.mak:
 
@@ -23,6 +24,9 @@  QEMU_DGFLAGS += -MMD -MP -MT $@ -MF $(*D)/$(*F).d
 %.o: %.m
 	$(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<,"  OBJC  $(TARGET_DIR)$@")
 
+%.o: %.cpp
+	$(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<,"  CXX   $(TARGET_DIR)$@")
+
 LINK = $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(1) $(LIBS),"  LINK  $(TARGET_DIR)$@")
 
 %$(EXESUF): %.o
@@ -39,7 +43,7 @@  quiet-command = $(if $(V),$1,$(if $(2),@echo $2 && $1, @$1))
 cc-option = $(if $(shell $(CC) $1 $2 -S -o /dev/null -xc /dev/null \
               >/dev/null 2>&1 && echo OK), $2, $3)
 
-VPATH_SUFFIXES = %.c %.h %.S %.m %.mak %.texi
+VPATH_SUFFIXES = %.c %.h %.S %.m %.cpp %.mak %.texi
 set-vpath = $(if $1,$(foreach PATTERN,$(VPATH_SUFFIXES),$(eval vpath $(PATTERN) $1)))
 
 # Generate timestamp files for .h include files
diff --git a/ui/haiku.cpp b/ui/haiku.cpp
new file mode 100644
index 0000000..37b9414
--- /dev/null
+++ b/ui/haiku.cpp
@@ -0,0 +1,607 @@ 
+/*
+ * QEMU Haiku display driver
+ *
+ * Copyright (c) 2010 Andreas Färber
+ * Copyright (c) 2005-2009 Michael Lotz
+ *
+ * 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 "haiku.h"
+
+#include <stdio.h>
+
+#include <Path.h>
+#include <Screen.h>
+#include <WindowScreen.h>
+
+
+extern "C" {
+
+#include "qemu-common.h"
+#include "console.h"
+#include "sysemu.h"
+
+int qemu_main(int argc, char **argv, char** envp);
+
+}
+
+
+QEMUApplication *gApplication;
+QEMUWindow *gWindow;
+QEMUView *gView;
+bool gFullScreen;
+int gWidth = 100;
+int gHeight = 100;
+BPoint gCenter;
+BPoint gPreviousLocation;
+
+
+// Haiku keycode to scancode table
+static const uint8
+haiku_to_pc_key[] = {
+	0x00,	/* 0x00 */						0x01,	/* 0x01 Esc */
+	0x3b,	/* 0x02 F1 */					0x3c,	/* 0x03 F2 */
+	0x3d,	/* 0x04 F3 */					0x3e,	/* 0x05 F4 */
+	0x3f,	/* 0x06 F5 */					0x40,	/* 0x07 F6 */
+	0x41,	/* 0x08 F7 */					0x42,	/* 0x09 F8 */
+	0x43,	/* 0x0a F9 */					0x44,	/* 0x0b F10 */
+	0x57,	/* 0x0c F11 */					0x58,	/* 0x0d F12 */
+	0xb7,	/* 0x0e Print Screen */			0x46,	/* 0x0f Scroll Lock */
+	0xc5,	/* 0x10 Pause */				0x29,	/* 0x11 Grave */
+	0x02,	/* 0x12 1 */					0x03,	/* 0x13 2 */
+	0x04,	/* 0x14 3 */					0x05,	/* 0x15 4 */
+	0x06,	/* 0x16 5 */					0x07,	/* 0x17 6 */
+	0x08,	/* 0x18 7 */					0x09,	/* 0x19 8 */
+	0x0a,	/* 0x1a 9 */					0x0b,	/* 0x1b 0 */
+	0x0c,	/* 0x1c Minus */				0x0d,	/* 0x1d Equals */
+	0x0e,	/* 0x1e Backspace */			0xd2,	/* 0x1f Insert */
+	0xc7,	/* 0x20 Home */					0xc9,	/* 0x21 Page Up */
+	0x45,	/* 0x22 Num Lock */				0xb5,	/* 0x23 KP Divide */
+	0x37,	/* 0x24 KP Multiply */			0x4a,	/* 0x25 KP Subtract */
+	0x0f,	/* 0x26 Tab */					0x10,	/* 0x27 Q */
+	0x11,	/* 0x28 W */					0x12,	/* 0x29 E */
+	0x13,	/* 0x2a R */					0x14,	/* 0x2b T */
+	0x15,	/* 0x2c Y */					0x16,	/* 0x2d U */
+	0x17,	/* 0x2e I */					0x18,	/* 0x2f O */
+	0x19,	/* 0x30 P */					0x1a,	/* 0x31 Left Bracket */
+	0x1b,	/* 0x32 Right Bracket */		0x2b,	/* 0x33 Backslash */
+	0xd3,	/* 0x34 Delete */				0xcf,	/* 0x35 End */
+	0xd1,	/* 0x36 Page Down */			0x47,	/* 0x37 KP 7 */
+	0x48,	/* 0x38 KP 8 */					0x49,	/* 0x39 KP 9 */
+	0x4e,	/* 0x3a KP Add */				0x3a,	/* 0x3b Caps Lock */
+	0x1e,	/* 0x3c A */					0x1f,	/* 0x3d S */
+	0x20,	/* 0x3e D */					0x21,	/* 0x3f F */
+	0x22,	/* 0x40 G */					0x23,	/* 0x41 H */
+	0x24,	/* 0x42 J */					0x25,	/* 0x43 K */
+	0x26,	/* 0x44 L */					0x27,	/* 0x45 Semicolon */
+	0x28,	/* 0x46 Single Quote */			0x1c,	/* 0x47 Enter */
+	0x4b,	/* 0x48 KP 4 */					0x4c,	/* 0x49 KP 5 */
+	0x4d,	/* 0x4a KP 6 */					0x2a,	/* 0x4b Left Shift */
+	0x2c,	/* 0x4c Z */					0x2d,	/* 0x4d X */
+	0x2e,	/* 0x4e C */					0x2f,	/* 0x4f V */
+	0x30,	/* 0x50 B */					0x31,	/* 0x51 N */
+	0x32,	/* 0x52 M */					0x33,	/* 0x53 Comma */
+	0x34,	/* 0x54 Period */				0x35,	/* 0x55 Slash */
+	0x36,	/* 0x56 Right Shift */			0xc8,	/* 0x57 Up */
+	0x4f,	/* 0x58 KP 1 */					0x50,	/* 0x59 KP 2 */
+	0x51,	/* 0x5a KP 3 */					0x9c,	/* 0x5b KP Enter */
+	0x1d,	/* 0x5c Left Control */			0x38,	/* 0x5d Left Alt */
+	0x39,	/* 0x5e Space */				0xb8,	/* 0x5f Right Alt */
+	0x9d,	/* 0x60 Right Control */		0xcb,	/* 0x61 Left */
+	0xd0,	/* 0x62 Down */					0xcd,	/* 0x63 Right */
+	0x52,	/* 0x64 KP 0 */					0x53,	/* 0x65 KP . */
+	0xdb,	/* 0x66 Left Windows */			0xdc,	/* 0x67 Right Windows */
+	0xdd,	/* 0x68 Menu */					0x56,	/* 0x69 */
+	0x7d,	/* 0x6a Macron */				0x73,	/* 0x6b Backslash */
+	0x7b,	/* 0x6c Muhenkan */				0x79,	/* 0x6d Henkan */
+	0x70,	/* 0x6e Hiragana Katakana */	0x00,	/* 0x6f */
+	0x00,	/* 0x70 */						0x00,	/* 0x71 */
+	0x00,	/* 0x72 */						0x00,	/* 0x73 */
+	0x00,	/* 0x74 */						0x00,	/* 0x75 */
+	0x00,	/* 0x76 */						0x00,	/* 0x77 */
+	0x00,	/* 0x78 */						0x00,	/* 0x79 */
+	0x00,	/* 0x7a */						0x00,	/* 0x7b */
+	0x00,	/* 0x7c */						0x00,	/* 0x7d */
+	0x54,	/* 0x7e Alt SysRq */			0xc6,	/* 0x7f Control Break */
+};
+
+
+int
+main(int argc, char **argv)
+{
+    for (int i = 1; i < argc; i++) {
+        if (strcmp(argv[i], "-nographic") == 0 ||
+            strcmp(argv[i], "-curses") == 0 ||
+            strcmp(argv[i], "-vnc") == 0) {
+        	return qemu_main(argc, argv, NULL);
+        }
+    }
+    QEMUApplication *app = new QEMUApplication(argc, argv);
+    app->Run();
+    delete app;
+    return 0;
+}
+
+
+QEMUApplication::QEMUApplication(int argc, char **argv)
+    :   BApplication("application/x-vnd.qemu.QEMU"),
+        fThread(0),
+        fWindow(NULL)
+{
+    gApplication = this;
+
+    fArgC = argc;
+    fArgV = argv;
+
+    fThread = spawn_thread(&RunQEMUMain, "qemu_main", B_LOW_PRIORITY, this);
+    resume_thread(fThread);
+}
+
+
+bool
+QEMUApplication::QuitRequested()
+{
+    qemu_system_shutdown_request();
+    return true;
+}
+
+
+void
+QEMUApplication::InitDisplay()
+{
+    fWindow = new QEMUWindow();
+    fWindow->Show();
+}
+
+
+int32
+QEMUApplication::RunQEMUMain(void *arg)
+{
+    QEMUApplication *app = (QEMUApplication *)arg;
+    qemu_main(app->fArgC, app->fArgV, NULL);
+    app->PostMessage(B_QUIT_REQUESTED);
+    return B_OK;
+}
+
+
+QEMUWindow::QEMUWindow()
+    :   BWindow(BRect(100, 100, 150, 150), "QEMU", B_TITLED_WINDOW,
+            B_QUIT_ON_WINDOW_CLOSE | B_NOT_RESIZABLE)
+{
+    gWindow = this;
+    fView = new QEMUView(Bounds());
+    AddChild(fView);
+    fView->MakeFocus();
+}
+
+
+QEMUView::QEMUView(BRect frame)
+    :   BView(frame, "fView", B_FOLLOW_ALL, B_WILL_DRAW),
+        fBitmap(NULL)
+{
+    gView = this;
+    fBitmap = new BBitmap(frame, 0, B_RGBA32);
+    AddFilter(new BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE, &MessageFilter));
+}
+
+
+QEMUView::~QEMUView()
+{
+    if (gFullScreen) {
+        BScreen().SetMode(&fDisplayMode);
+    }
+}
+
+
+void
+QEMUView::UpdateFullScreen()
+{
+    if (!gFullScreen) {
+        return;
+    }
+
+    BScreen screen;
+    display_mode current;
+    display_mode *modes;
+    display_mode *mode;
+    uint32 count;
+
+    screen.GetMode(&current);
+    screen.GetModeList(&modes, &count);
+
+    for (uint32 i = 0; i < count; i++) {
+        mode = &modes[i];
+        if (mode->virtual_width == gWidth
+            && mode->virtual_height == gHeight
+            && mode->space == current.space) {
+            screen.SetMode(mode);
+            break;
+        }
+    }
+
+    gWindow->MoveTo(0, 0);
+}
+
+
+void
+QEMUView::CenterMouse(bool &warp)
+{
+    BRect window = gWindow->Frame();
+    BPoint center = window.LeftTop() + gCenter;
+    set_mouse_position((int32)center.x, (int32)center.y);
+    warp = true;
+}
+
+
+void
+QEMUView::StartGrab(bool &grab)
+{
+    gApplication->HideCursor();
+    gWindow->SetTitle("QEMU - Press Ctrl-Alt to exit grab");
+    grab = true;
+}
+
+
+void
+QEMUView::EndGrab(bool &grab, int32 modifiers)
+{
+    gApplication->ShowCursor();
+    gWindow->SetTitle("QEMU");
+    grab = false;
+
+	// reset any set modifiers
+	if (modifiers & B_LEFT_SHIFT_KEY)
+		kbd_put_keycode(0xaa);
+	if (modifiers & B_RIGHT_SHIFT_KEY)
+		kbd_put_keycode(0xb6);
+
+	if (modifiers & B_LEFT_COMMAND_KEY)
+		kbd_put_keycode(0xb8);
+	if (modifiers & B_RIGHT_COMMAND_KEY) {
+		kbd_put_keycode(0xe0);
+		kbd_put_keycode(0xb8);
+	}
+
+	if (modifiers & B_LEFT_CONTROL_KEY)
+		kbd_put_keycode(0x9d);
+	if (modifiers & B_RIGHT_CONTROL_KEY) {
+		kbd_put_keycode(0xe0);
+		kbd_put_keycode(0x9d);
+	}
+
+	if (modifiers & B_LEFT_OPTION_KEY) {
+		kbd_put_keycode(0xe0);
+		kbd_put_keycode(0xdb);
+	}
+	if (modifiers & B_RIGHT_OPTION_KEY) {
+		kbd_put_keycode(0xe0);
+		kbd_put_keycode(0xdc);
+	}
+}
+
+
+filter_result
+QEMUView::MessageFilter(BMessage *message, BHandler **target,
+	BMessageFilter *filter)
+{
+	static bool sGrabInput = false;
+	static bool sMouseWarp = false;
+	static int32 sMouseButtons = 0;
+	bool keyDown = false;
+
+	switch (message->what) {
+		case B_KEY_DOWN:
+		case B_UNMAPPED_KEY_DOWN:
+			keyDown = true;
+			// fall
+
+		case B_KEY_UP:
+		case B_UNMAPPED_KEY_UP: {
+			int32 modifiers;
+			message->FindInt32("modifiers", &modifiers);
+
+			int32 key;
+			message->FindInt32("key", &key);
+			uint8 keycode = haiku_to_pc_key[(uint8)key];
+
+			int32 mask = (B_COMMAND_KEY | B_CONTROL_KEY);
+			if (!keyDown && sGrabInput && (modifiers & mask)
+				&& (key == 0x5d || key == 0x5c)) {
+					EndGrab(sGrabInput, modifiers);
+					return B_SKIP_MESSAGE;
+			}
+
+			if (keyDown && (modifiers & mask) == mask) {
+				switch (key) {
+					case 0x3f: { /* f - fullscreen */
+						BScreen screen;
+						if (!gFullScreen) {
+							screen.GetMode(&gView->fDisplayMode);
+							gWindow->MoveTo(0, 0);
+							gFullScreen = true;
+						} else {
+							screen.SetMode(&gView->fDisplayMode);
+							gWindow->MoveTo(gView->fWindowLocation);
+							gFullScreen = false;
+						}
+
+						UpdateFullScreen();
+						if (is_graphic_console()) {
+							vga_hw_invalidate();
+							vga_hw_update();
+						}
+
+						return B_SKIP_MESSAGE;
+					} break;
+
+					case 0x52: { /* m - pseudo fullscreen */
+						BPoint location = gWindow->Frame().LeftTop();
+						if (location.x == 0 && location.y == 0)
+							gWindow->MoveTo(gPreviousLocation);
+						else {
+							gPreviousLocation = location;
+							gWindow->MoveTo(0, 0);
+						}
+
+						if (is_graphic_console()) {
+							vga_hw_invalidate();
+							vga_hw_update();
+						}
+
+						return B_SKIP_MESSAGE;
+					} break;
+
+					case 0x12 ... 0x1a: { /* 1 to 9 - switch console */
+						console_select(key - 0x12);
+						if (is_graphic_console()) {
+							vga_hw_invalidate();
+							vga_hw_update();
+						}
+
+						return B_SKIP_MESSAGE;
+					} break;
+				}
+			} else if (!is_graphic_console()) {
+				if (!keyDown)
+					return B_SKIP_MESSAGE;
+
+				int32 rawChar;
+				message->FindInt32("raw_char", &rawChar);
+
+				int keysym = 0;
+				if (modifiers & (B_LEFT_CONTROL_KEY | B_RIGHT_CONTROL_KEY)) {
+					switch(rawChar) {
+						case B_UP_ARROW: keysym = QEMU_KEY_CTRL_UP; break;
+						case B_DOWN_ARROW: keysym = QEMU_KEY_CTRL_DOWN; break;
+						case B_LEFT_ARROW: keysym = QEMU_KEY_CTRL_LEFT; break;
+						case B_RIGHT_ARROW: keysym = QEMU_KEY_CTRL_RIGHT; break;
+						case B_HOME: keysym = QEMU_KEY_CTRL_HOME; break;
+						case B_END: keysym = QEMU_KEY_CTRL_END; break;
+						case B_PAGE_UP: keysym = QEMU_KEY_CTRL_PAGEUP; break;
+						case B_PAGE_DOWN: keysym = QEMU_KEY_CTRL_PAGEDOWN; break;
+					}
+				} else {
+					switch(rawChar) {
+						case B_UP_ARROW: keysym = QEMU_KEY_UP; break;
+						case B_DOWN_ARROW: keysym = QEMU_KEY_DOWN; break;
+						case B_LEFT_ARROW: keysym = QEMU_KEY_LEFT; break;
+						case B_RIGHT_ARROW: keysym = QEMU_KEY_RIGHT; break;
+						case B_HOME: keysym = QEMU_KEY_HOME; break;
+						case B_END: keysym = QEMU_KEY_END; break;
+						case B_PAGE_UP: keysym = QEMU_KEY_PAGEUP; break;
+						case B_PAGE_DOWN: keysym = QEMU_KEY_PAGEDOWN; break;
+						case B_BACKSPACE: keysym = QEMU_KEY_BACKSPACE; break;
+						case B_DELETE: keysym = QEMU_KEY_DELETE; break;
+					}
+				}
+
+				if (keysym)
+					kbd_put_keysym(keysym);
+				else {
+					const char *bytes;
+					if (message->FindString("bytes", &bytes) == B_OK && bytes[0] != 0)
+						kbd_put_keysym(bytes[0]);
+				}
+				return B_SKIP_MESSAGE;
+			}
+
+			if (keycode & 0x80)
+				kbd_put_keycode(0xe0);
+
+			if (keyDown)
+				kbd_put_keycode(keycode & 0x7f);
+			else
+				kbd_put_keycode(keycode | 0x80);
+
+			return B_SKIP_MESSAGE;
+		} break;
+
+		case B_MOUSE_MOVED: {
+			if (!sGrabInput)
+				break;
+
+			if (sMouseWarp) {
+				sMouseWarp = false;
+				return B_SKIP_MESSAGE;
+			}
+
+			BPoint where;
+			message->FindPoint("where", &where);
+			BPoint delta = where - gCenter;
+
+			// Haiku buttons are the same as QEMU
+			kbd_mouse_event((int)delta.x, (int)delta.y, 0, sMouseButtons);
+
+			CenterMouse(sMouseWarp);
+			return B_SKIP_MESSAGE;
+		} break;
+
+		case B_MOUSE_DOWN:
+		case B_MOUSE_UP: {
+			int32 buttons;
+			message->FindInt32("buttons", &buttons);
+
+			if (!sGrabInput) {
+				if (message->what == B_MOUSE_DOWN
+					&& (buttons & B_PRIMARY_MOUSE_BUTTON)) {
+					CenterMouse(sMouseWarp);
+					StartGrab(sGrabInput);
+				}
+				break;
+			}
+
+			sMouseButtons = buttons;
+			// Haiku buttons are the same as QEMU
+			kbd_mouse_event(0, 0, 0, sMouseButtons);
+			return B_SKIP_MESSAGE;
+		} break;
+
+		case B_MOUSE_WHEEL_CHANGED: {
+			if (!sGrabInput)
+				break;
+
+			float delta;
+			message->FindFloat("be:wheel_delta_y", &delta);
+			kbd_mouse_event(0, 0, (int)delta, sMouseButtons);
+			return B_SKIP_MESSAGE;
+		} break;
+	}
+
+	return B_DISPATCH_MESSAGE;
+}
+
+
+void
+QEMUView::Update(BPoint point, int width, int height)
+{
+    LockLooper();
+    fBitmap->ImportBits(fFrameBuffer, fFrameBufferSize, fBytesPerRow,
+        fColorSpace, point, point, width, height);
+
+    Draw(BRect(point.x, point.y, point.x + width, point.y + height));
+    UnlockLooper();
+}
+
+
+void
+QEMUView::Draw(BRect updateRect)
+{
+    if (fBitmap == NULL) {
+        return;
+    }
+
+    DrawBitmap(fBitmap, updateRect, updateRect);
+}
+
+
+void
+QEMUView::UpdateFrameBuffer(int width, int height, uchar *bits,
+	int bytesPerRow, int bitsPerPixel)
+{
+	if (LockLooper()) {
+		delete fBitmap;
+		fBitmap = new BBitmap(BRect(0, 0, width - 1, height - 1), 0, B_RGBA32);
+		fFrameBuffer = bits;
+		fFrameBufferSize = bytesPerRow * height;
+		fBytesPerRow = bytesPerRow;
+
+		switch (bitsPerPixel) {
+			case 32:
+				fColorSpace = B_RGB32;
+				break;
+			case 24:
+				fColorSpace = B_RGB24;
+				break;
+			case 16:
+				fColorSpace = B_RGB16;
+				break;
+			case 15:
+				fColorSpace = B_RGB15;
+				break;
+			case 8:
+				fColorSpace = B_CMAP8;
+				break;
+			default:
+				printf("unsupported display depth %d\n", bitsPerPixel);
+				break;
+		}
+
+		UnlockLooper();
+	}
+}
+
+
+// QEMU C interface
+extern "C" {
+
+static void haiku_update(DisplayState *ds, int x, int y, int w, int h);
+static void haiku_resize(DisplayState *ds);
+static void haiku_refresh(DisplayState *ds);
+
+};
+
+
+static void
+haiku_update(DisplayState *ds, int x, int y, int w, int h)
+{
+    //printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h);
+    gView->Update(BPoint(x, y), w, h);
+}
+
+
+static void
+haiku_resize(DisplayState *ds)
+{
+    //printf("resizing\n");
+    gWidth = ds_get_width(ds);
+    gHeight = ds_get_height(ds);
+    gCenter.x = (int32)(gWidth / 2);
+    gCenter.y = (int32)(gHeight / 2);
+    gWindow->ResizeTo(gWidth - 1, gHeight - 1);
+    gWindow->SetZoomLimits(gWidth, gHeight);
+    gView->UpdateFrameBuffer(ds_get_width(ds), ds_get_height(ds),
+        ds_get_data(ds), ds_get_linesize(ds), ds_get_bits_per_pixel(ds));
+    gView->UpdateFullScreen();
+}
+
+
+static void
+haiku_refresh(DisplayState *ds)
+{
+    //printf("refreshing\n");
+    if (is_graphic_console()) {
+        vga_hw_update();
+    }
+}
+
+
+void
+haiku_display_init(DisplayState *ds, int fullScreen)
+{
+    gApplication->InitDisplay();
+    gFullScreen = fullScreen != 0;
+
+    DisplayChangeListener *displayChangeListener
+        = (DisplayChangeListener *)qemu_mallocz(sizeof(DisplayChangeListener));
+    displayChangeListener->dpy_update = haiku_update;
+    displayChangeListener->dpy_resize = haiku_resize;
+    displayChangeListener->dpy_refresh = haiku_refresh;
+    register_displaychangelistener(ds, displayChangeListener);
+}
diff --git a/ui/haiku.h b/ui/haiku.h
new file mode 100644
index 0000000..2c342d2
--- /dev/null
+++ b/ui/haiku.h
@@ -0,0 +1,96 @@ 
+/*
+ * QEMU Haiku display driver
+ * 
+ * Copyright (c) 2005-2009 Michael Lotz
+ * 
+ * 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.
+ */
+
+#ifndef _HAIKU_H_
+#define _HAIKU_H_
+
+#include <Accelerant.h>
+#include <Application.h>
+#include <Bitmap.h>
+#include <MessageFilter.h>
+#include <View.h>
+#include <Window.h>
+
+class QEMUWindow;
+class QEMUView;
+
+class QEMUApplication : public BApplication {
+public:
+							QEMUApplication(int argc, char **argv);
+
+virtual	bool				QuitRequested();
+		void				InitDisplay();
+
+private:
+static	int32				RunQEMUMain(void *arg);
+
+		int					fArgC;
+		char **				fArgV;
+
+		thread_id			fThread;
+		QEMUWindow *		fWindow;
+};
+
+
+class QEMUWindow : public BWindow {
+public:
+							QEMUWindow();
+
+private:
+		QEMUView *			fView;
+};
+
+
+class QEMUView : public BView {
+public:
+							QEMUView(BRect frame);
+virtual						~QEMUView();
+
+		void				Update(BPoint point, int width, int height);
+virtual	void				Draw(BRect updateRect);
+
+		void				UpdateFrameBuffer(int width, int height,
+								uchar *bits, int bytesPerRow,
+								int bitsPerPixel);
+
+static	void				UpdateFullScreen();
+static	void				CenterMouse(bool &warp);
+static	void				StartGrab(bool &grab);
+static	void				EndGrab(bool &grab, int32 modifiers);
+
+private:
+static	filter_result		MessageFilter(BMessage *message, BHandler **target,
+								BMessageFilter *filter);
+
+		BBitmap *			fBitmap;
+		BPoint				fWindowLocation;
+		display_mode		fDisplayMode;
+
+		uint8 *				fFrameBuffer;
+		uint32				fFrameBufferSize;
+		uint32				fBytesPerRow;
+		color_space			fColorSpace;
+};
+
+#endif
diff --git a/vl.c b/vl.c
index d352d18..7641763 100644
--- a/vl.c
+++ b/vl.c
@@ -1799,6 +1799,10 @@  static const QEMUOption *lookup_opt(int argc, char **argv,
     return popt;
 }
 
+#ifdef CONFIG_HAIKU
+#define main qemu_main
+#endif
+
 int main(int argc, char **argv, char **envp)
 {
     const char *gdbstub_dev = NULL;
@@ -2919,7 +2923,7 @@  int main(int argc, char **argv, char **envp)
     ds = get_displaystate();
 
     if (display_type == DT_DEFAULT) {
-#if defined(CONFIG_SDL) || defined(CONFIG_COCOA)
+#if defined(CONFIG_SDL) || defined(CONFIG_COCOA) || defined(CONFIG_HAIKU)
         display_type = DT_SDL;
 #else
         display_type = DT_VNC;
@@ -2945,6 +2949,10 @@  int main(int argc, char **argv, char **envp)
     case DT_SDL:
         cocoa_display_init(ds, full_screen);
         break;
+#elif defined(CONFIG_HAIKU)
+    case DT_SDL:
+        haiku_display_init(ds, full_screen);
+        break;
 #endif
     case DT_VNC:
         vnc_display_init(ds);