diff --git a/app-emulation/qemu/patches/qemu-8.1.3-haiku.patchset b/app-emulation/qemu/patches/qemu-8.1.3-haiku.patchset new file mode 100644 index 000000000..8c6487248 --- /dev/null +++ b/app-emulation/qemu/patches/qemu-8.1.3-haiku.patchset @@ -0,0 +1,1546 @@ +From 8992b8aa94622dd7a841fcc58e9d454ce9e44926 Mon Sep 17 00:00:00 2001 +From: David Karoly +Date: Sat, 4 Feb 2023 13:39:11 +0100 +Subject: introduce Haiku ui + + +diff --git a/meson.build b/meson.build +index a9c4f28..d391b65 100644 +--- a/meson.build ++++ b/meson.build +@@ -29,6 +29,10 @@ if targetos == 'darwin' and \ + all_languages += ['objc'] + objc = meson.get_compiler('objc') + endif ++if targetos == 'haiku' and add_languages('cpp', required: false, native: false) ++ all_languages += ['cpp'] ++ cxx = meson.get_compiler('cpp') ++endif + + # Temporary directory used for files created while + # configure runs. Since it is in the build directory +@@ -611,6 +615,7 @@ hvf = not_found + midl = not_found + widl = not_found + pathcch = not_found ++haiku = not_found + host_dsosuf = '.so' + if targetos == 'windows' + midl = find_program('midl', required: false) +@@ -636,6 +641,11 @@ elif targetos == 'haiku' + socket = [cc.find_library('posix_error_mapper'), + cc.find_library('network'), + cc.find_library('bsd')] ++ if not get_option('haiku').disabled() ++ haikulibs = [cc.find_library('be'), ++ cc.find_library('game')] ++ haiku = declare_dependency(dependencies: haikulibs) ++ endif + elif targetos == 'openbsd' + if get_option('tcg').allowed() and target_dirs.length() > 0 + # Disable OpenBSD W^X if available +@@ -2075,6 +2085,7 @@ config_host_data.set('CONFIG_BRLAPI', brlapi.found()) + config_host_data.set('CONFIG_COCOA', cocoa.found()) + config_host_data.set('CONFIG_FUZZ', get_option('fuzzing')) + config_host_data.set('CONFIG_GCOV', get_option('b_coverage')) ++config_host_data.set('CONFIG_HAIKU', haiku.found()) + config_host_data.set('CONFIG_LIBUDEV', libudev.found()) + config_host_data.set('CONFIG_LZO', lzo.found()) + config_host_data.set('CONFIG_MPATH', mpathpersist.found()) +@@ -4220,6 +4231,9 @@ summary_info = {} + if targetos == 'darwin' + summary_info += {'Cocoa support': cocoa} + endif ++if targetos == 'haiku' ++ summary_info += {'Haiku support': haiku} ++endif + summary_info += {'SDL support': sdl} + summary_info += {'SDL image support': sdl_image} + summary_info += {'GTK support': gtk} +diff --git a/meson_options.txt b/meson_options.txt +index ae6d8f4..3e7725c 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -138,6 +138,8 @@ option('bpf', type : 'feature', value : 'auto', + description: 'eBPF support') + option('cocoa', type : 'feature', value : 'auto', + description: 'Cocoa user interface (macOS only)') ++option('haiku', type : 'feature', value : 'auto', ++ description: 'Haiku user interface (Haiku only)') + option('curl', type : 'feature', value : 'auto', + description: 'CURL block device driver') + option('gio', type : 'feature', value : 'auto', +diff --git a/qapi/ui.json b/qapi/ui.json +index 006616a..5fb6789 100644 +--- a/qapi/ui.json ++++ b/qapi/ui.json +@@ -1487,6 +1487,7 @@ + { 'name': 'egl-headless', 'if': 'CONFIG_OPENGL' }, + { 'name': 'curses', 'if': 'CONFIG_CURSES' }, + { 'name': 'cocoa', 'if': 'CONFIG_COCOA' }, ++ { 'name': 'haiku', 'if': 'CONFIG_HAIKU' }, + { 'name': 'spice-app', 'if': 'CONFIG_SPICE' }, + { 'name': 'dbus', 'if': 'CONFIG_DBUS_DISPLAY' } + ] +diff --git a/ui/console.c b/ui/console.c +index bca610b..91a438a 100644 +--- a/ui/console.c ++++ b/ui/console.c +@@ -2766,6 +2766,9 @@ bool qemu_display_find_default(DisplayOptions *opts) + #endif + #if defined(CONFIG_COCOA) + DISPLAY_TYPE_COCOA ++#endif ++#if defined(CONFIG_HAIKU) ++ DISPLAY_TYPE_HAIKU + #endif + }; + int i; +diff --git a/ui/haiku.cpp b/ui/haiku.cpp +new file mode 100644 +index 0000000..ab7eb6c +--- /dev/null ++++ b/ui/haiku.cpp +@@ -0,0 +1,908 @@ ++/* ++ * QEMU Haiku display driver ++ * ++ * Copyright (c) 2005-2013 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 ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++struct QemuConsole; ++ ++static QEMUApplication *sApplication = NULL; ++static QEMUWindow *sWindow = NULL; ++static QEMUView *sView = NULL; ++static bool sFullScreen = false; ++static QemuConsole *sCurrentConsole = NULL; ++static bool sGraphicConsole = false; ++static bool sAbsoluteMouse = false; ++static bool sGrabInput = false; ++static int sWidth = 100; ++static int sHeight = 100; ++static BPoint sCenter; ++static BPoint sPreviousLocation; ++static thread_id sMainThread = -1; ++ ++static const uint32 kMessageBlockSignals = 'bksg'; ++ ++static const uint32 kKeycodeEvent = 'keyc'; ++static const uint32 kKeysymEvent = 'keys'; ++static const uint32 kMouseEvent = 'mous'; ++static const uint32 kConsoleSelectEvent = 'cons'; ++static const uint32 kShutdownRequest = 'shut'; ++static const uint32 kInvalidationRequest = 'ival'; ++ ++ ++// QEMU C interface ++extern "C" { ++#define new _new ++#define class _class ++#define typename _typename ++ // Guard against reserved keywords in C includes ++ ++#include "qemu/osdep.h" ++#include "qemu/main-loop.h" ++#include "qemu-main.h" ++#include "ui/console.h" ++#include "ui/input.h" ++ ++#undef new ++#undef class ++#undef typename ++ ++static void haiku_refresh(DisplayChangeListener *dcl); ++static void haiku_update(DisplayChangeListener *dcl, int x, int y, int w, ++ int h); ++static void haiku_switch(DisplayChangeListener *dcl, ++ DisplaySurface *newSurface); ++ ++ void qemu_system_shutdown_request(void); ++}; ++ ++ ++// 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 */ ++}; ++ ++ ++// Key constants from vl.h ++#define QEMU_KEY_ESC1(c) ((c) | 0xe100) ++#define QEMU_KEY_BACKSPACE 0x007f ++#define QEMU_KEY_UP QEMU_KEY_ESC1('A') ++#define QEMU_KEY_DOWN QEMU_KEY_ESC1('B') ++#define QEMU_KEY_RIGHT QEMU_KEY_ESC1('C') ++#define QEMU_KEY_LEFT QEMU_KEY_ESC1('D') ++#define QEMU_KEY_HOME QEMU_KEY_ESC1(1) ++#define QEMU_KEY_END QEMU_KEY_ESC1(4) ++#define QEMU_KEY_PAGEUP QEMU_KEY_ESC1(5) ++#define QEMU_KEY_PAGEDOWN QEMU_KEY_ESC1(6) ++#define QEMU_KEY_DELETE QEMU_KEY_ESC1(3) ++ ++#define QEMU_KEY_CTRL_UP 0xe400 ++#define QEMU_KEY_CTRL_DOWN 0xe401 ++#define QEMU_KEY_CTRL_LEFT 0xe402 ++#define QEMU_KEY_CTRL_RIGHT 0xe403 ++#define QEMU_KEY_CTRL_HOME 0xe404 ++#define QEMU_KEY_CTRL_END 0xe405 ++#define QEMU_KEY_CTRL_PAGEUP 0xe406 ++#define QEMU_KEY_CTRL_PAGEDOWN 0xe407 ++ ++ ++static void ++haiku_register_display_change_listener(void) ++{ ++ static const DisplayChangeListenerOps sDisplayChangeListenerOps = { ++ .dpy_name = "haiku", ++ .dpy_refresh = haiku_refresh, ++ .dpy_gfx_update = haiku_update, ++ .dpy_gfx_switch = haiku_switch, ++ }; ++ ++ static DisplayChangeListener sDisplayChangeListener = { ++ .ops = &sDisplayChangeListenerOps, ++ }; ++ ++ register_displaychangelistener(&sDisplayChangeListener); ++} ++ ++ ++static void ++block_signals(bool block) ++{ ++ sigset_t set; ++ sigemptyset(&set); ++ sigaddset(&set, SIGUSR1); ++ sigaddset(&set, SIGUSR2); ++ sigaddset(&set, SIGALRM); ++ pthread_sigmask(block ? SIG_BLOCK : SIG_UNBLOCK, &set, NULL); ++} ++ ++ ++static int ++haiku_main(void) ++{ ++ block_signals(true); ++ sMainThread = find_thread(NULL); ++ QEMUApplication *app = new QEMUApplication(); ++ ++ sApplication->InitDisplay(); ++ haiku_register_display_change_listener(); ++ ++ app->Run(); ++ delete app; ++ return 0; ++} ++ ++ ++QEMUApplication::QEMUApplication() ++ : BApplication("application/x-vnd.mmlr.QEMU"), ++ fThread(0), ++ fWindow(NULL) ++{ ++ sApplication = this; ++ ++ qemu_mutex_unlock_iothread(); ++ fThread = spawn_thread(&RunQEMUMain, "qemu_main", B_LOW_PRIORITY, this); ++ resume_thread(fThread); ++} ++ ++ ++bool ++QEMUApplication::QuitRequested() ++{ ++ if (sView != NULL) ++ sView->QueueShutdownRequest(); ++ return true; ++} ++ ++ ++void ++QEMUApplication::InitDisplay() ++{ ++ fWindow = new QEMUWindow(); ++ fWindow->Show(); ++ ++ BMessage reply; ++ BMessenger(fWindow->Looper()).SendMessage(kMessageBlockSignals , &reply); ++} ++ ++ ++int32 ++QEMUApplication::RunQEMUMain(void *arg) ++{ ++ QEMUApplication *app = (QEMUApplication *)arg; ++ ++ block_signals(false); ++ ++ qemu_mutex_lock_iothread(); ++ qemu_default_main(); ++ qemu_mutex_unlock_iothread(); ++ ++ 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) ++{ ++ sWindow = this; ++ fView = new QEMUView(Bounds()); ++ AddChild(fView); ++} ++ ++ ++void ++QEMUWindow::MessageReceived(BMessage *message) ++{ ++ if (message->what == kMessageBlockSignals) { ++ block_signals(true); ++ return; ++ } ++ ++ BWindow::MessageReceived(message); ++} ++ ++ ++QEMUView::QEMUView(BRect frame) ++ : BView(frame, "fView", B_FOLLOW_ALL, B_WILL_DRAW), ++ fBitmap(NULL) ++{ ++ sView = this; ++ fBitmap = new BBitmap(frame, 0, B_RGBA32); ++ AddFilter(new BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE, &MessageFilter)); ++} ++ ++ ++QEMUView::~QEMUView() ++{ ++ if (sFullScreen) ++ BScreen().SetMode(&fDisplayMode); ++} ++ ++ ++void ++QEMUView::AttachedToWindow() ++{ ++ MakeFocus(); ++} ++ ++ ++void ++QEMUView::UpdateFullScreen() ++{ ++ if (!sFullScreen) ++ return; ++ ++ BScreen screen; ++ display_mode current; ++ display_mode *modes; ++ display_mode *mode; ++ uint32 count; ++ ++ screen.GetMode(¤t); ++ screen.GetModeList(&modes, &count); ++ ++ for (uint32 i = 0; i < count; i++) { ++ mode = &modes[i]; ++ if (mode->virtual_width == sWidth ++ && mode->virtual_height == sHeight ++ && mode->space == current.space) { ++ screen.SetMode(mode); ++ break; ++ } ++ } ++ ++ sWindow->MoveTo(0, 0); ++} ++ ++ ++void ++QEMUView::CenterMouse(bool &warp) ++{ ++ BRect window = sWindow->Frame(); ++ BPoint center = window.LeftTop() + sCenter; ++ set_mouse_position((int32)center.x, (int32)center.y); ++ warp = true; ++} ++ ++ ++void ++QEMUView::StartGrab(bool &grab) ++{ ++ sApplication->HideCursor(); ++ sWindow->SetTitle("QEMU - Press Ctrl-Alt to exit grab"); ++ grab = true; ++} ++ ++ ++void ++QEMUView::EndGrab(bool &grab, int32 modifiers) ++{ ++ sApplication->ShowCursor(); ++ sWindow->SetTitle("QEMU"); ++ grab = false; ++ ++ // reset any set modifiers ++ if (modifiers & B_LEFT_SHIFT_KEY) ++ QueueKeycode(0x2a, false); ++ if (modifiers & B_RIGHT_SHIFT_KEY) ++ QueueKeycode(0x36, false); ++ ++ if (modifiers & B_LEFT_COMMAND_KEY) ++ QueueKeycode(0x38, false); ++ if (modifiers & B_RIGHT_COMMAND_KEY) ++ QueueKeycode(0xb8, false); ++ ++ if (modifiers & B_LEFT_CONTROL_KEY) ++ QueueKeycode(0x1d, false); ++ if (modifiers & B_RIGHT_CONTROL_KEY) ++ QueueKeycode(0x9d, false); ++ ++ if (modifiers & B_LEFT_OPTION_KEY) ++ QueueKeycode(0xdb, false); ++ if (modifiers & B_RIGHT_OPTION_KEY) ++ QueueKeycode(0xdc, false); ++} ++ ++ ++void ++QEMUView::QueueEvent(BMessage *event) ++{ ++ if (!fEventQueue.Lock()) ++ return; ++ ++ fEventQueue.AddMessage(event); ++ fEventQueue.Unlock(); ++} ++ ++ ++void ++QEMUView::QueueKeycode(uint8 keycode, bool keyDown) ++{ ++ BMessage *event = new BMessage(kKeycodeEvent); ++ event->AddUInt8("keycode", keycode); ++ event->AddBool("keyDown", keyDown); ++ sView->QueueEvent(event); ++} ++ ++ ++void ++QEMUView::QueueKeysym(int32 keysym) ++{ ++ BMessage *event = new BMessage(kKeysymEvent); ++ event->AddInt32("keysym", keysym); ++ sView->QueueEvent(event); ++} ++ ++ ++void ++QEMUView::QueueMouseEvent(BPoint where, int32 deltaZ, int32 buttonState) ++{ ++ int32 deltaX = where.x; ++ int32 deltaY = where.y; ++ ++ if (!sAbsoluteMouse) { ++ deltaX -= sCenter.x; ++ deltaY -= sCenter.y; ++ } ++ ++ BMessage *event = new BMessage(kMouseEvent); ++ event->AddInt32("deltaX", deltaX); ++ event->AddInt32("deltaY", deltaY); ++ event->AddInt32("deltaZ", deltaZ); ++ event->AddInt32("buttonState", buttonState); ++ sView->QueueEvent(event); ++} ++ ++ ++void ++QEMUView::QueueShutdownRequest() ++{ ++ sView->QueueEvent(new BMessage(kShutdownRequest)); ++} ++ ++ ++void ++QEMUView::QueueConsoleSelect(uint8 console) ++{ ++ BMessage *event = new BMessage(kConsoleSelectEvent); ++ event->AddUInt8("console", console); ++ sView->QueueEvent(event); ++} ++ ++ ++void ++QEMUView::QueueInvalidation() ++{ ++ sView->QueueEvent(new BMessage(kInvalidationRequest)); ++} ++ ++ ++void ++QEMUView::ProcessEvents() ++{ ++ while (true) { ++ if (!fEventQueue.Lock()) ++ return; ++ ++ BMessage *event = fEventQueue.NextMessage(); ++ fEventQueue.Unlock(); ++ ++ if (event == NULL) ++ return; ++ ++ switch (event->what) { ++ case kKeycodeEvent: ++ { ++ uint8 keycode = 0; ++ bool keyDown = false; ++ if (event->FindUInt8("keycode", &keycode) != B_OK ++ || event->FindBool("keyDown", &keyDown) != B_OK) { ++ break; ++ } ++ ++ qemu_input_event_send_key_number(sCurrentConsole, keycode, ++ keyDown); ++ break; ++ } ++ ++ case kKeysymEvent: ++ { ++ int32 keysym = 0; ++ if (event->FindInt32("keysym", &keysym) != B_OK) ++ break; ++ ++ kbd_put_keysym(keysym); ++ break; ++ } ++ ++ case kMouseEvent: ++ { ++ int32 deltaX = 0; ++ int32 deltaY = 0; ++ int32 deltaZ = 0; ++ int32 buttonState = 0; ++ if (event->FindInt32("deltaX", &deltaX) != B_OK ++ || event->FindInt32("deltaY", &deltaY) != B_OK ++ || event->FindInt32("deltaZ", &deltaZ) != B_OK ++ || event->FindInt32("buttonState", &buttonState) != B_OK) { ++ break; ++ } ++ ++ if (sAbsoluteMouse) { ++ qemu_input_queue_abs(sCurrentConsole, INPUT_AXIS_X, deltaX, ++ 0, sWidth); ++ qemu_input_queue_abs(sCurrentConsole, INPUT_AXIS_Y, deltaY, ++ 0, sHeight); ++ } else { ++ qemu_input_queue_rel(sCurrentConsole, INPUT_AXIS_X, deltaX); ++ qemu_input_queue_rel(sCurrentConsole, INPUT_AXIS_Y, deltaY); ++ } ++ ++ static int32 lastButtonState = 0; ++ if (lastButtonState != buttonState) { ++ // Haiku buttons are the same as QEMU ++ static uint32_t buttonMap[INPUT_BUTTON__MAX] = { ++ [INPUT_BUTTON_LEFT] = B_PRIMARY_MOUSE_BUTTON, ++ [INPUT_BUTTON_MIDDLE] = B_TERTIARY_MOUSE_BUTTON, ++ [INPUT_BUTTON_RIGHT] = B_SECONDARY_MOUSE_BUTTON ++ }; ++ ++ qemu_input_update_buttons(sCurrentConsole, buttonMap, ++ lastButtonState, buttonState); ++ lastButtonState = buttonState; ++ } ++ ++ qemu_input_event_sync(); ++ ++ if (deltaZ != 0) { ++ qemu_input_queue_btn(sCurrentConsole, deltaZ < 0 ++ ? INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN, ++ true); ++ qemu_input_event_sync(); ++ qemu_input_queue_btn(sCurrentConsole, deltaZ < 0 ++ ? INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN, ++ false); ++ qemu_input_event_sync(); ++ } ++ ++ break; ++ } ++ ++ case kShutdownRequest: ++ qemu_system_shutdown_request(); ++ break; ++ ++ case kConsoleSelectEvent: ++ { ++ uint8 console = 0; ++ if (event->FindUInt8("console", &console) != B_OK) ++ break; ++ ++ console_select(console); ++ sCurrentConsole = qemu_console_lookup_by_index(console); ++ sGraphicConsole = qemu_console_is_graphic(NULL); ++ break; ++ } ++ ++ case kInvalidationRequest: ++ graphic_hw_invalidate(NULL); ++ graphic_hw_update(NULL); ++ break; ++ } ++ ++ delete event; ++ } ++} ++ ++ ++filter_result ++QEMUView::MessageFilter(BMessage *message, BHandler **target, ++ BMessageFilter *filter) ++{ ++ static bool sMouseWarp = false; ++ static int32 sMouseButtons = 0; ++ static BPoint sMousePosition; ++ 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 (!sFullScreen) { ++ screen.GetMode(&sView->fDisplayMode); ++ sWindow->MoveTo(0, 0); ++ sFullScreen = true; ++ } else { ++ screen.SetMode(&sView->fDisplayMode); ++ sWindow->MoveTo(sView->fWindowLocation); ++ sFullScreen = false; ++ } ++ ++ UpdateFullScreen(); ++ QueueInvalidation(); ++ return B_SKIP_MESSAGE; ++ } break; ++ ++ case 0x52: { /* m - pseudo fullscreen */ ++ BPoint location = sWindow->Frame().LeftTop(); ++ if (location.x == 0 && location.y == 0) ++ sWindow->MoveTo(sPreviousLocation); ++ else { ++ sPreviousLocation = location; ++ sWindow->MoveTo(0, 0); ++ } ++ ++ QueueInvalidation(); ++ return B_SKIP_MESSAGE; ++ } break; ++ ++ case 0x12 ... 0x1a: { /* 1 to 9 - switch console */ ++ QueueConsoleSelect(key - 0x12); ++ QueueInvalidation(); ++ return B_SKIP_MESSAGE; ++ } break; ++ } ++ } else if (!sGraphicConsole) { ++ 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) ++ QueueKeysym(keysym); ++ else { ++ const char *bytes; ++ if (message->FindString("bytes", &bytes) == B_OK && bytes[0] != 0) ++ QueueKeysym(bytes[0]); ++ } ++ return B_SKIP_MESSAGE; ++ } ++ ++ QueueKeycode(keycode, keyDown); ++ return B_SKIP_MESSAGE; ++ } break; ++ ++ case B_MOUSE_MOVED: { ++ if (!sGrabInput && !sAbsoluteMouse) ++ break; ++ ++ if (sMouseWarp) { ++ sMouseWarp = false; ++ return B_SKIP_MESSAGE; ++ } ++ ++ int32 transit; ++ if (message->FindInt32("be:transit", &transit) == B_OK ++ && transit != B_ENTERED_VIEW && transit != B_INSIDE_VIEW) { ++ break; ++ } ++ ++ message->FindPoint("where", &sMousePosition); ++ ++ QueueMouseEvent(sMousePosition, 0, sMouseButtons); ++ ++ if (!sAbsoluteMouse) ++ CenterMouse(sMouseWarp); ++ ++ return B_SKIP_MESSAGE; ++ } break; ++ ++ case B_MOUSE_DOWN: ++ case B_MOUSE_UP: { ++ int32 buttons; ++ message->FindInt32("buttons", &buttons); ++ ++ if (!sGrabInput && !sAbsoluteMouse) { ++ if (message->what == B_MOUSE_DOWN ++ && (buttons & B_PRIMARY_MOUSE_BUTTON)) { ++ CenterMouse(sMouseWarp); ++ StartGrab(sGrabInput); ++ } ++ break; ++ } ++ ++ sMouseButtons = buttons; ++ message->FindPoint("where", &sMousePosition); ++ ++ QueueMouseEvent(sMousePosition, 0, sMouseButtons); ++ return B_SKIP_MESSAGE; ++ } break; ++ ++ case B_MOUSE_WHEEL_CHANGED: { ++ if (!sGrabInput && !sAbsoluteMouse) ++ break; ++ ++ float delta; ++ message->FindFloat("be:wheel_delta_y", &delta); ++ ++ QueueMouseEvent(sMousePosition, (int32)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, BSize(width, height)); ++ ++ Invalidate(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(); ++ } ++} ++ ++ ++static void ++haiku_refresh(DisplayChangeListener *dcl) ++{ ++ //printf(">haiku_refresh\n"); ++ sView->ProcessEvents(); ++ if (sGraphicConsole) ++ graphic_hw_update(NULL); ++ //printf("haiku_update x=%d y=%d w=%d h=%d\n", x, y, w, h); ++ sView->Update(BPoint(x, y), w, h); ++ //printf("haiku_switch\n"); ++ sWidth = surface_width(newSurface); ++ sHeight = surface_height(newSurface); ++ sCenter.x = (int32)(sWidth / 2); ++ sCenter.y = (int32)(sHeight / 2); ++ sWindow->ResizeTo(sWidth - 1, sHeight - 1); ++ sWindow->SetZoomLimits(sWidth, sHeight); ++ sView->UpdateFrameBuffer(surface_width(newSurface), ++ surface_height(newSurface), (uint8 *)surface_data(newSurface), ++ surface_stride(newSurface), surface_bits_per_pixel(newSurface)); ++ sView->UpdateFullScreen(); ++ //printf("EndGrab(sGrabInput, 0); ++ sApplication->SetCursor(&cursor, true); ++ } else ++ sApplication->SetCursor(&cursor, true); ++} ++ ++ ++void ++haiku_display_init(DisplayState *ds, DisplayOptions *o) ++{ ++ qemu_main = haiku_main; ++ ++ sFullScreen = (o->has_full_screen && o->full_screen); ++ sGraphicConsole = qemu_console_is_graphic(NULL); ++ sAbsoluteMouse = qemu_input_is_absolute(); ++ ++ static Notifier sMouseModeChangeNotifier = { haiku_mouse_mode_change }; ++ qemu_add_mouse_mode_change_notifier(&sMouseModeChangeNotifier); ++} ++ ++static QemuDisplay qemu_display_haiku = { ++ .type = DISPLAY_TYPE_HAIKU, ++ .init = haiku_display_init, ++}; ++ ++static void register_haiku(void) ++{ ++ qemu_display_register(&qemu_display_haiku); ++} ++ ++type_init(register_haiku); +diff --git a/ui/haiku.h b/ui/haiku.h +new file mode 100644 +index 0000000..74ff982 +--- /dev/null ++++ b/ui/haiku.h +@@ -0,0 +1,113 @@ ++/* ++ * QEMU Haiku display driver ++ * ++ * Copyright (c) 2005-2013 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 "haiku-include-before.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++class QEMUWindow; ++class QEMUView; ++ ++class QEMUApplication : public BApplication { ++public: ++ QEMUApplication(); ++ ++virtual bool QuitRequested(); ++ void InitDisplay(); ++ ++private: ++static int32 RunQEMUMain(void *arg); ++ ++ thread_id fThread; ++ QEMUWindow * fWindow; ++}; ++ ++ ++class QEMUWindow : public BWindow { ++public: ++ QEMUWindow(); ++ ++virtual void MessageReceived(BMessage *message); ++ ++private: ++ QEMUView * fView; ++}; ++ ++ ++class QEMUView : public BView { ++public: ++ QEMUView(BRect frame); ++virtual ~QEMUView(); ++ ++virtual void AttachedToWindow(); ++ ++ 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); ++ ++static void QueueKeycode(uint8 keycode, bool keyDown); ++static void QueueKeysym(int32 keysym); ++static void QueueMouseEvent(BPoint where, int32 deltaZ, ++ int32 buttonState); ++static void QueueShutdownRequest(); ++static void QueueConsoleSelect(uint8 console); ++static void QueueInvalidation(); ++ ++ void ProcessEvents(); ++ ++private: ++ void QueueEvent(BMessage *event); ++ ++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; ++ ++ BMessageQueue fEventQueue; ++}; ++ ++#endif +diff --git a/ui/meson.build b/ui/meson.build +index d81609f..6513f4d 100644 +--- a/ui/meson.build ++++ b/ui/meson.build +@@ -30,6 +30,11 @@ system_ss.add(when: 'CONFIG_LINUX', if_true: files( + )) + system_ss.add(when: cocoa, if_true: files('cocoa.m')) + ++if haiku.found() ++ system_ss.add(haiku) ++ system_ss.add(files('haiku.cpp')) ++endif ++ + vnc_ss = ss.source_set() + vnc_ss.add(files( + 'vnc.c', +-- +2.42.0 + + +From 53b22816af9582967a773fa8ede091f2a6da9695 Mon Sep 17 00:00:00 2001 +From: David Karoly +Date: Sat, 4 Feb 2023 13:43:10 +0100 +Subject: regenerate meson build options + + +diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh +index d7020af..ac28cb4 100644 +--- a/scripts/meson-buildoptions.sh ++++ b/scripts/meson-buildoptions.sh +@@ -110,6 +110,7 @@ meson_options_help() { + printf "%s\n" ' gtk-clipboard clipboard support for the gtk UI (EXPERIMENTAL, MAY HANG)' + printf "%s\n" ' guest-agent Build QEMU Guest Agent' + printf "%s\n" ' guest-agent-msi Build MSI package for the QEMU Guest Agent' ++ printf "%s\n" ' haiku Haiku user interface (Haiku only)' + printf "%s\n" ' hax HAX acceleration support' + printf "%s\n" ' hvf HVF acceleration support' + printf "%s\n" ' iconv Font glyph conversion support' +@@ -313,6 +314,8 @@ _meson_option_parse() { + --disable-guest-agent) printf "%s" -Dguest_agent=disabled ;; + --enable-guest-agent-msi) printf "%s" -Dguest_agent_msi=enabled ;; + --disable-guest-agent-msi) printf "%s" -Dguest_agent_msi=disabled ;; ++ --enable-haiku) printf "%s" -Dhaiku=enabled ;; ++ --disable-haiku) printf "%s" -Dhaiku=disabled ;; + --enable-hax) printf "%s" -Dhax=enabled ;; + --disable-hax) printf "%s" -Dhax=disabled ;; + --enable-hexagon-idef-parser) printf "%s" -Dhexagon_idef_parser=true ;; +-- +2.42.0 + + +From 8093a2ba6fd1872d5e8ae62b892c426a141bc61e Mon Sep 17 00:00:00 2001 +From: David Karoly +Date: Sat, 4 Feb 2023 13:43:27 +0100 +Subject: introduce Haiku audio backend + + +diff --git a/audio/audio.c b/audio/audio.c +index 90c7c49..cb53355 100644 +--- a/audio/audio.c ++++ b/audio/audio.c +@@ -2052,6 +2052,9 @@ void audio_create_pdos(Audiodev *dev) + #ifdef CONFIG_AUDIO_DSOUND + CASE(DSOUND, dsound, ); + #endif ++#ifdef CONFIG_AUDIO_HAIKU ++ CASE(HAIKU, haiku, ); ++#endif + #ifdef CONFIG_AUDIO_JACK + CASE(JACK, jack, Jack); + #endif +diff --git a/audio/audio.h b/audio/audio.h +index 01bdc56..c2b39c8 100644 +--- a/audio/audio.h ++++ b/audio/audio.h +@@ -27,8 +27,10 @@ + + #include "qemu/queue.h" + #include "qapi/qapi-types-audio.h" ++#ifndef __cplusplus + #include "hw/qdev-properties.h" + #include "hw/qdev-properties-system.h" ++#endif + + typedef void (*audio_callback_fn) (void *opaque, int avail); + +@@ -154,7 +156,7 @@ uint64_t AUD_get_elapsed_usec_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts); + + static inline void *advance (void *p, int incr) + { +- uint8_t *d = p; ++ uint8_t *d = (uint8_t *)p; + return (d + incr); + } + +diff --git a/audio/audio_template.h b/audio/audio_template.h +index dc0c74a..b97e066 100644 +--- a/audio/audio_template.h ++++ b/audio/audio_template.h +@@ -350,6 +350,10 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev) + case AUDIODEV_DRIVER_DSOUND: + return dev->u.dsound.TYPE; + #endif ++#ifdef CONFIG_AUDIO_HAIKU ++ case AUDIODEV_DRIVER_HAIKU: ++ return dev->u.haiku.TYPE; ++#endif + #ifdef CONFIG_AUDIO_JACK + case AUDIODEV_DRIVER_JACK: + return qapi_AudiodevJackPerDirectionOptions_base(dev->u.jack.TYPE); +diff --git a/audio/haikuaudio.cpp b/audio/haikuaudio.cpp +new file mode 100644 +index 0000000..1cd662c +--- /dev/null ++++ b/audio/haikuaudio.cpp +@@ -0,0 +1,194 @@ ++/* ++ * QEMU Haiku audio output driver ++ * ++ * Copyright (c) 2005-2013 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. ++ */ ++ ++extern "C" { ++#include "qemu/osdep.h" ++#include "qemu/module.h" ++ ++#include "audio/audio.h" ++ ++#define AUDIO_CAP "haiku" ++#include "audio_int.h" ++ ++static int haiku_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque); ++static void haiku_fini_out(HWVoiceOut *hw); ++static void haiku_enable_out(HWVoiceOut *hw, bool enable); ++ ++static void * haiku_audio_init(Audiodev *dev); ++static void haiku_audio_fini(void *opaque); ++ ++static void register_audio_haiku(void); ++} ++ ++#include ++ ++typedef struct HaikuVoiceOut { ++ HWVoiceOut hw; ++ BSoundPlayer *player; ++ Audiodev *dev; ++} HaikuVoiceOut; ++ ++ ++void ++PlayBuffer(void *cookie, void *buffer, size_t size, const media_raw_audio_format &format) ++{ ++ HaikuVoiceOut *voice = (HaikuVoiceOut *)cookie; ++ HWVoiceOut *hw = &voice->hw; ++ ++ while (hw->pending_emul && size) { ++ size_t start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul); ++ assert(start < hw->size_emul); ++ ++ size_t write_len = MIN(MIN(hw->pending_emul, size), hw->size_emul - start); ++ memcpy(buffer, hw->buf_emul + start, write_len); ++ ++ hw->pending_emul -= write_len; ++ size -= write_len; ++ buffer = (uint8_t *)buffer + write_len; ++ } ++ ++ if (size) ++ audio_pcm_info_clear_buf(&hw->info, buffer, size / hw->info.bytes_per_frame); ++} ++ ++ ++static uint32 ++aud_to_haikufmt(AudioFormat fmt, int endianness) ++{ ++ switch (fmt) { ++ case AUDIO_FORMAT_U8: ++ return media_raw_audio_format::B_AUDIO_UCHAR; ++ case AUDIO_FORMAT_S8: ++ return media_raw_audio_format::B_AUDIO_CHAR; ++ case AUDIO_FORMAT_S16: ++ return media_raw_audio_format::B_AUDIO_SHORT; ++ case AUDIO_FORMAT_S32: ++ return media_raw_audio_format::B_AUDIO_INT; ++ case AUDIO_FORMAT_U16: ++ case AUDIO_FORMAT_U32: ++ printf("Unsigned 16/32 bit audio format not supported!\n"); ++ return 1; ++ default: ++ dolog("Internal logic error: Bad audio format %d\n", fmt); ++#ifdef DEBUG_AUDIO ++ abort(); ++#endif ++ return media_raw_audio_format::B_AUDIO_UCHAR; ++ } ++} ++ ++ ++static int ++haiku_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) ++{ ++ HaikuVoiceOut *voice = (HaikuVoiceOut *)hw; ++ Audiodev *dev = (Audiodev *)drv_opaque; ++ ++ media_raw_audio_format format; ++ format.frame_rate = as->freq; ++ format.channel_count = as->nchannels; ++ format.byte_order = B_MEDIA_LITTLE_ENDIAN; ++ format.buffer_size = audio_buffer_bytes(dev->u.haiku.out, as, 11610); ++ format.format = aud_to_haikufmt(as->fmt, as->endianness); ++ ++ audio_pcm_init_info(&hw->info, as); ++ hw->samples = 4 * audio_buffer_samples(dev->u.haiku.out, as, 11610); ++ ++ voice->dev = dev; ++ voice->player = new BSoundPlayer(&format, "QEMU", PlayBuffer, NULL, voice); ++ voice->player->Start(); ++ voice->player->SetHasData(true); ++ ++ return 0; ++} ++ ++ ++static void ++haiku_fini_out(HWVoiceOut *hw) ++{ ++ HaikuVoiceOut *voice = (HaikuVoiceOut *)hw; ++ ++ voice->player->Stop(); ++ delete voice->player; ++ voice->player = NULL; ++} ++ ++ ++static void ++haiku_enable_out(HWVoiceOut *hw, bool enable) ++{ ++ HaikuVoiceOut *voice = (HaikuVoiceOut *)hw; ++ if (!voice->player) ++ return; ++ ++ if (enable) { ++ ldebug("enabling voice\n"); ++ voice->player->SetHasData(true); ++ } else { ++ ldebug("disabling voice\n"); ++ //voice->player->SetHasData(false); ++ } ++} ++ ++ ++static void * ++haiku_audio_init(Audiodev *dev) ++{ ++ // just an opaque handle, doesn't matter, we don't use it ++ return (void *)dev; ++} ++ ++ ++static void ++haiku_audio_fini(void *opaque) ++{ ++} ++ ++ ++static struct audio_pcm_ops haiku_pcm_ops = { ++ .init_out = haiku_init_out, ++ .fini_out = haiku_fini_out, ++ .write = audio_generic_write, ++ .enable_out = haiku_enable_out, ++}; ++ ++ ++static struct audio_driver haiku_audio_driver = { ++ .name = "haiku", ++ .descr = "Haiku Native Audio", ++ .init = haiku_audio_init, ++ .fini = haiku_audio_fini, ++ .pcm_ops = &haiku_pcm_ops, ++ .can_be_default = 1, ++ .max_voices_out = 1, ++ .max_voices_in = 0, ++ .voice_size_out = sizeof(HaikuVoiceOut), ++ .voice_size_in = 0 ++}; ++ ++static void register_audio_haiku(void) ++{ ++ audio_driver_register(&haiku_audio_driver); ++} ++type_init(register_audio_haiku); +diff --git a/audio/meson.build b/audio/meson.build +index df4d968..f94b053 100644 +--- a/audio/meson.build ++++ b/audio/meson.build +@@ -18,6 +18,7 @@ foreach m : [ + ['pa', pulse, files('paaudio.c')], + ['sdl', sdl, files('sdlaudio.c')], + ['jack', jack, files('jackaudio.c')], ++ ['haiku', haiku, files('haikuaudio.cpp')], + ['sndio', sndio, files('sndioaudio.c')], + ['pipewire', pipewire, files('pwaudio.c')], + ['spice', spice, files('spiceaudio.c')] +diff --git a/meson.build b/meson.build +index d391b65..8f39515 100644 +--- a/meson.build ++++ b/meson.build +@@ -643,7 +643,8 @@ elif targetos == 'haiku' + cc.find_library('bsd')] + if not get_option('haiku').disabled() + haikulibs = [cc.find_library('be'), +- cc.find_library('game')] ++ cc.find_library('game'), ++ cc.find_library('media')] + haiku = declare_dependency(dependencies: haikulibs) + endif + elif targetos == 'openbsd' +@@ -1911,6 +1912,7 @@ if have_system + 'alsa': alsa.found(), + 'coreaudio': coreaudio.found(), + 'dsound': dsound.found(), ++ 'haiku': haiku.found(), + 'jack': jack.found(), + 'oss': oss.found(), + 'pa': pulse.found(), +@@ -1924,7 +1926,7 @@ if have_system + + # Default to native drivers first, OSS second, SDL third + audio_drivers_priority = \ +- [ 'pa', 'coreaudio', 'dsound', 'sndio', 'oss' ] + \ ++ [ 'pa', 'coreaudio', 'dsound', 'haiku', 'sndio', 'oss' ] + \ + (targetos == 'linux' ? [] : [ 'sdl' ]) + audio_drivers_default = [] + foreach k: audio_drivers_priority +diff --git a/qapi/audio.json b/qapi/audio.json +index 519697c..c71bb91 100644 +--- a/qapi/audio.json ++++ b/qapi/audio.json +@@ -455,6 +455,7 @@ + { 'name': 'coreaudio', 'if': 'CONFIG_AUDIO_COREAUDIO' }, + { 'name': 'dbus', 'if': 'CONFIG_DBUS_DISPLAY' }, + { 'name': 'dsound', 'if': 'CONFIG_AUDIO_DSOUND' }, ++ { 'name': 'haiku', 'if': 'CONFIG_AUDIO_HAIKU' }, + { 'name': 'jack', 'if': 'CONFIG_AUDIO_JACK' }, + { 'name': 'oss', 'if': 'CONFIG_AUDIO_OSS' }, + { 'name': 'pa', 'if': 'CONFIG_AUDIO_PA' }, +@@ -494,6 +495,8 @@ + 'if': 'CONFIG_DBUS_DISPLAY' }, + 'dsound': { 'type': 'AudiodevDsoundOptions', + 'if': 'CONFIG_AUDIO_DSOUND' }, ++ 'haiku': { 'type': 'AudiodevGenericOptions', ++ 'if': 'CONFIG_AUDIO_HAIKU' }, + 'jack': { 'type': 'AudiodevJackOptions', + 'if': 'CONFIG_AUDIO_JACK' }, + 'oss': { 'type': 'AudiodevOssOptions', +-- +2.42.0 + + +From c848a5081cf5e0a04a12313c4a3a1dea88e028bd Mon Sep 17 00:00:00 2001 +From: David Karoly +Date: Fri, 8 Dec 2023 17:08:37 +0100 +Subject: kludge haiku.cpp + + +diff --git a/ui/haiku.cpp b/ui/haiku.cpp +index ab7eb6c..281ab7f 100644 +--- a/ui/haiku.cpp ++++ b/ui/haiku.cpp +@@ -68,7 +68,9 @@ extern "C" { + // Guard against reserved keywords in C includes + + #include "qemu/osdep.h" +-#include "qemu/main-loop.h" ++// main-loop.h does not work very well when including it from a C++ souce file ++// therefore the required function prototypes are re-declared below ++//#include "qemu/main-loop.h" + #include "qemu-main.h" + #include "ui/console.h" + #include "ui/input.h" +@@ -77,6 +79,12 @@ extern "C" { + #undef class + #undef typename + ++#define qemu_mutex_lock_iothread() \ ++ qemu_mutex_lock_iothread_impl(__FILE__, __LINE__) ++ ++void qemu_mutex_lock_iothread_impl(const char *file, int line); ++void qemu_mutex_unlock_iothread(void); ++ + static void haiku_refresh(DisplayChangeListener *dcl); + static void haiku_update(DisplayChangeListener *dcl, int x, int y, int w, + int h); +-- +2.42.1 + diff --git a/app-emulation/qemu/patches/qemu-8.1.3.patchset b/app-emulation/qemu/patches/qemu-8.1.3.patchset new file mode 100644 index 000000000..3f52c9b90 --- /dev/null +++ b/app-emulation/qemu/patches/qemu-8.1.3.patchset @@ -0,0 +1,69 @@ +From 213c05df476769966f2db0884315646787616f0f Mon Sep 17 00:00:00 2001 +From: Alexander von Gluck IV +Date: Tue, 18 May 2021 16:49:20 -0500 +Subject: haiku: fixes and patches, rebased from qemu 3.x + + +diff --git a/disas/nanomips.c b/disas/nanomips.c +index a025359..e877de2 100644 +--- a/disas/nanomips.c ++++ b/disas/nanomips.c +@@ -30,10 +30,14 @@ + #include "qemu/osdep.h" + #include "disas/dis-asm.h" + ++#ifndef __HAIKU__ + typedef int64_t int64; + typedef uint64_t uint64; + typedef uint32_t uint32; + typedef uint16_t uint16; ++#else ++#include ++#endif + typedef uint64_t img_address; + + typedef enum { +diff --git a/util/notify.c b/util/notify.c +index 76bab21..7c1ea84 100644 +--- a/util/notify.c ++++ b/util/notify.c +@@ -67,6 +67,10 @@ int notifier_with_return_list_notify(NotifierWithReturnList *list, void *data) + int ret = 0; + + QLIST_FOREACH_SAFE(notifier, &list->notifiers, node, next) { ++ #ifdef __HAIKU__ ++ if(notifier->notify == NULL) break; ++ #endif ++ + ret = notifier->notify(notifier, data); + if (ret != 0) { + break; +-- +2.42.0 + + +From 2a539ebf7f9fa0d8f2d59fdebfd062b4de16b8ed Mon Sep 17 00:00:00 2001 +From: David Karoly +Date: Fri, 8 Dec 2023 15:49:29 +0100 +Subject: Haiku: fix build + + +diff --git a/meson.build b/meson.build +index a9c4f28..319ade2 100644 +--- a/meson.build ++++ b/meson.build +@@ -277,9 +277,9 @@ endif + # instead, we can't add -no-pie because it overrides -shared: the linker then + # tries to build an executable instead of a shared library and fails. So + # don't add -no-pie anywhere and cross fingers. :( +-if not get_option('b_pie') +- qemu_common_flags += cc.get_supported_arguments('-fno-pie', '-no-pie') +-endif ++#if not get_option('b_pie') ++# qemu_common_flags += cc.get_supported_arguments('-fno-pie', '-no-pie') ++#endif + + if not get_option('stack_protector').disabled() + stack_protector_probe = ''' +-- +2.42.1 diff --git a/app-emulation/qemu/qemu-8.1.3.recipe b/app-emulation/qemu/qemu-8.1.3.recipe new file mode 100644 index 000000000..19c4a207d --- /dev/null +++ b/app-emulation/qemu/qemu-8.1.3.recipe @@ -0,0 +1,251 @@ +SUMMARY="A generic and open source machine emulator and virtualizer" +DESCRIPTION="QEMU is a generic and open source machine emulator and virtualizer. + +QEMU can run OSes and programs made for one machine (e.g. an ARM board) on a \ +different machine (e.g. your own PC). By using dynamic translation, it \ +achieves very good performance." +HOMEPAGE="https://www.qemu.org/" +COPYRIGHT="2003-2023 Fabrice Bellard" +LICENSE="GNU GPL v2" +REVISION="1" +SOURCE_URI="https://download.qemu.org/qemu-$portVersion.tar.xz" +CHECKSUM_SHA256="43cc176804105586f74f90398f34e9f85787dff400d3b640d81f7779fbe265bb" +SOURCE_DIR="qemu-$portVersion" +PATCHES=" + qemu-$portVersion.patchset + qemu-$portVersion-haiku.patchset + " +ADDITIONAL_FILES="qemu.rdef.in" + +ARCHITECTURES="all !x86_gcc2" +SECONDARY_ARCHITECTURES="x86" + +commandSuffix=$secondaryArchSuffix +commandBinDir=$binDir +if [ "$targetArchitecture" = x86_gcc2 ]; then + commandSuffix= + commandBinDir=$prefix/bin +fi + +platformList="x86 arm mips ppc sparc m68k riscv" +archList_x86="i386 x86_64" +archList_arm="arm aarch64" +archList_mips="mips mipsel mips64 mips64el" +archList_ppc="ppc ppc64" +archList_sparc="sparc sparc64" +archList_m68k="m68k" +archList_riscv="riscv32 riscv64" + +PROVIDES=" + qemu$secondaryArchSuffix = $portVersion + cmd:elf2dmp$commandSuffix = $portVersion + cmd:qemu_edid$commandSuffix = $portVersion + cmd:qemu_keymap$commandSuffix = $portVersion + cmd:qemu_img$commandSuffix = $portVersion + cmd:qemu_io$commandSuffix = $portVersion + cmd:qemu_nbd$commandSuffix = $portVersion + cmd:qemu_storage_daemon$commandSuffix = $portVersion + " +REQUIRES=" + haiku$secondaryArchSuffix + cmd:iasl + cmd:smbd$secondaryArchSuffix + lib:libbz2$secondaryArchSuffix + lib:libcapstone$secondaryArchSuffix + lib:libcurl$secondaryArchSuffix + lib:libfdt$secondaryArchSuffix + lib:libgcrypt$secondaryArchSuffix + lib:libglib_2.0$secondaryArchSuffix + lib:libgnutls$secondaryArchSuffix + lib:libgpg_error$secondaryArchSuffix + lib:libintl$secondaryArchSuffix + lib:libjpeg$secondaryArchSuffix + lib:liblzo2$secondaryArchSuffix + lib:libncursesw$secondaryArchSuffix + lib:libnettle$secondaryArchSuffix + lib:libpixman_1$secondaryArchSuffix + lib:libpng16$secondaryArchSuffix + lib:libSDL2_2.0$secondaryArchSuffix + lib:libsnappy$secondaryArchSuffix + lib:libssh2$secondaryArchSuffix + lib:libssp$secondaryArchSuffix + lib:libtasn1$secondaryArchSuffix + lib:libusb_1.0$secondaryArchSuffix + lib:libxml2$secondaryArchSuffix + lib:libz$secondaryArchSuffix + " + +PROVIDES_x86=" + qemu${secondaryArchSuffix}_x86 = $portVersion + cmd:qemu$commandSuffix = $portVersion + cmd:qemu_system_i386$commandSuffix = $portVersion + cmd:qemu_system_x86_64$commandSuffix = $portVersion + " +REQUIRES_x86=" + $REQUIRES + qemu$secondaryArchSuffix + lib:libiconv$secondaryArchSuffix + lib:libslirp$secondaryArchSuffix + " +SUPPLEMENTS_x86=" + qemu$secondaryArchSuffix + " + +PROVIDES_arm=" + qemu${secondaryArchSuffix}_arm = $portVersion + cmd:qemu_system_aarch64$commandSuffix = $portVersion + cmd:qemu_system_arm$commandSuffix = $portVersion + " +REQUIRES_arm=$REQUIRES_x86 + +PROVIDES_mips=" + qemu${secondaryArchSuffix}_mips = $portVersion + cmd:qemu_system_mips$commandSuffix = $portVersion + cmd:qemu_system_mips64$commandSuffix = $portVersion + cmd:qemu_system_mips64el$commandSuffix = $portVersion + cmd:qemu_system_mipsel$commandSuffix = $portVersion + " +REQUIRES_mips=$REQUIRES_x86 + +PROVIDES_ppc=" + qemu${secondaryArchSuffix}_ppc = $portVersion + cmd:qemu_system_ppc$commandSuffix = $portVersion + cmd:qemu_system_ppc64$commandSuffix = $portVersion + " +REQUIRES_ppc=$REQUIRES_x86 + +PROVIDES_sparc=" + qemu${secondaryArchSuffix}_sparc = $portVersion + cmd:qemu_system_sparc$commandSuffix = $portVersion + cmd:qemu_system_sparc64$commandSuffix = $portVersion + " +REQUIRES_sparc=$REQUIRES_x86 + +PROVIDES_m68k=" + qemu${secondaryArchSuffix}_m68k = $portVersion + cmd:qemu_system_m68k$commandSuffix = $portVersion + " +REQUIRES_m68k=$REQUIRES_x86 + +PROVIDES_riscv=" + qemu${secondaryArchSuffix}_riscv = $portVersion + cmd:qemu_system_riscv32$commandSuffix = $portVersion + cmd:qemu_system_riscv64$commandSuffix = $portVersion + " +REQUIRES_riscv=$REQUIRES_x86 + +BUILD_REQUIRES=" + devel:libbz2$secondaryArchSuffix + devel:libcapstone$secondaryArchSuffix + devel:libcurl$secondaryArchSuffix + devel:libfdt$secondaryArchSuffix + devel:libgcrypt$secondaryArchSuffix + devel:libglib_2.0$secondaryArchSuffix + devel:libgnutls$secondaryArchSuffix + devel:libgpg_error$secondaryArchSuffix + devel:libintl$secondaryArchSuffix + devel:libjpeg$secondaryArchSuffix + devel:liblzo2$secondaryArchSuffix + devel:libncursesw$secondaryArchSuffix + devel:libnettle$secondaryArchSuffix + devel:libpixman_1$secondaryArchSuffix + devel:libpng16$secondaryArchSuffix + devel:libSDL2_2.0$secondaryArchSuffix + devel:libslirp$secondaryArchSuffix + devel:libsnappy$secondaryArchSuffix + devel:libssh2$secondaryArchSuffix + devel:libtasn1$secondaryArchSuffix + devel:libusb_1.0$secondaryArchSuffix + devel:libxml2$secondaryArchSuffix + devel:libz$secondaryArchSuffix + " +BUILD_PREREQUIRES=" + haiku${secondaryArchSuffix}_devel + cmd:awk + cmd:cmp + cmd:git + cmd:gcc$secondaryArchSuffix + cmd:gcov$secondaryArchSuffix + cmd:gprof$secondaryArchSuffix + cmd:ld$secondaryArchSuffix + cmd:make + cmd:ninja + cmd:find + cmd:iasl + cmd:pkg_config$secondaryArchSuffix + cmd:pod2man + cmd:python3 + " + +BUILD() +{ + export CFLAGS="-I`finddir B_SYSTEM_HEADERS_DIRECTORY`/capstone" + + for platformItem in $platformList; do + platformArchList=$(eval echo \$archList_$platformItem) + archList="$archList $platformArchList" + done + + for archItem in $archList; do + targetList="$archItem-softmmu,$targetList" + done + + runConfigure --omit-dirs "binDir sbinDir" \ + --omit-buildspec ./configure \ + --bindir="$commandBinDir" --sbindir="$commandBinDir" \ + --localedir=data/locale \ + --enable-gcrypt \ + --disable-gtk \ + --smbd=/$relativeBinDir/smbd \ + --target-list="${targetList::-1}" + + cd build + ninja $jobArgs +} + +INSTALL() +{ + cd build + ninja install $jobArgs + + local MAJOR="`echo "$portVersion" | cut -d. -f1`" + local MIDDLE="`echo "$portVersion" | cut -d. -f2`" + local MINOR="`echo "$portVersion" | cut -d. -f3`" + local LONG_INFO="$SUMMARY" + + archList="" + + for platformItem in $platformList; do + platformArchList=$(eval echo \$archList_$platformItem) + archList="$archList $platformArchList" + done + + for archItem in $archList; do + echo $archItem + local APP_SIGNATURE="application/x-vnd.qemu-system-$archItem" + sed \ + -e "s|@APP_SIGNATURE@|$APP_SIGNATURE|" \ + -e "s|@MAJOR@|$MAJOR|" \ + -e "s|@MIDDLE@|$MIDDLE|" \ + -e "s|@MINOR@|$MINOR|" \ + -e "s|@LONG_INFO@|$LONG_INFO|" \ + $portDir/additional-files/qemu.rdef.in > qemu-system-$archItem.rdef + addResourcesToBinaries qemu-system-$archItem.rdef $commandBinDir/qemu-system-$archItem + done + + # provide convenience symlink to just "qemu" + if [ "$effectiveTargetArchitecture" = x86_64 ]; then + ln -s $commandBinDir/qemu-system-x86_64 $commandBinDir/qemu + else + ln -s $commandBinDir/qemu-system-i386 $commandBinDir/qemu + fi + + packageEntries x86 $commandBinDir/qemu + + for platformItem in $platformList; do + platformArchList=$(eval echo \$archList_$platformItem) + for archItem in $platformArchList; do + packageEntries $platformItem $commandBinDir/qemu-system-$archItem + done + done +}