From 8a2684ff554d711222c0d82697de27dec9b0cb52 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 18 Sep 2018 09:27:16 +0200 Subject: [PATCH] Dreamcast mouse emulation --- core/hw/maple/maple_cfg.cpp | 6 +- core/hw/maple/maple_devs.cpp | 95 +++++++++++++ core/hw/maple/maple_devs.h | 1 + core/linux-dist/x11.cpp | 254 +++++++++++++++++++++++++++-------- core/linux-dist/x11.h | 2 +- core/nullDC.cpp | 4 + core/types.h | 6 + 7 files changed, 308 insertions(+), 60 deletions(-) diff --git a/core/hw/maple/maple_cfg.cpp b/core/hw/maple/maple_cfg.cpp index 7fe45fb19..05f1ee296 100644 --- a/core/hw/maple/maple_cfg.cpp +++ b/core/hw/maple/maple_cfg.cpp @@ -85,8 +85,10 @@ int numberOfControl = cfgLoadInt("players", "nb", 1); for (int i = 0; i < numberOfControl; i++){ mcfg_Create(MDT_SegaController, i, 5); } - if (numberOfControl < 4) - mcfg_Create(MDT_Keyboard, numberOfControl, 5); + if (settings.input.DCKeyboard && numberOfControl < 4) + mcfg_Create(MDT_Keyboard, numberOfControl++, 5); + if (settings.input.DCMouse != 0 && numberOfControl < 4) + mcfg_Create(MDT_Mouse, numberOfControl++, 5); mcfg_Create(MDT_SegaVMU,0,0); mcfg_Create(MDT_SegaVMU,0,1); diff --git a/core/hw/maple/maple_devs.cpp b/core/hw/maple/maple_devs.cpp index 44078d31c..e493c3784 100755 --- a/core/hw/maple/maple_devs.cpp +++ b/core/hw/maple/maple_devs.cpp @@ -1005,6 +1005,97 @@ struct maple_keyboard : maple_base } }; +u32 mo_buttons = 0xFFFFFFFF; +f32 mo_x_delta; +f32 mo_y_delta; +f32 mo_wheel_delta; + +struct maple_mouse : maple_base +{ + static u16 mo_cvt(f32 delta) + { + delta+=0x200; + if (delta<=0) + delta=0; + else if (delta>0x3FF) + delta=0x3FF; + + return (u16) delta; + } + + virtual u32 dma(u32 cmd) + { + switch (cmd) + { + case MDC_DeviceRequest: + //caps + //4 + w32(MFID_9_Mouse); + + //struct data + //3*4 + w32(0x00060700); // Mouse, 2 buttons, 4 axes + w32(0); + w32(0); + //1 area code + w8(0xFF); + //1 direction + w8(0); + // Product name (30) + for (u32 i = 0; i < 30; i++) + { + w8((u8)maple_sega_mouse_name[i]); + } + + // License (60) + for (u32 i = 0; i < 60; i++) + { + w8((u8)maple_sega_brand[i]); + } + + // Low-consumption standby current (2) + w16(0x0069); + + // Maximum current consumption (2) + w16(0x0120); + + return MDRS_DeviceStatus; + + case MDCF_GetCondition: + w32(MFID_9_Mouse); + //struct data + //int32 buttons ; digital buttons bitfield (little endian) + w32(mo_buttons); + //int16 axis1 ; horizontal movement (0-$3FF) (little endian) + w16(mo_cvt(mo_x_delta)); + //int16 axis2 ; vertical movement (0-$3FF) (little endian) + w16(mo_cvt(mo_y_delta)); + //int16 axis3 ; mouse wheel movement (0-$3FF) (little endian) + w16(mo_cvt(mo_wheel_delta)); + //int16 axis4 ; ? movement (0-$3FF) (little endian) + w16(mo_cvt(0)); + //int16 axis5 ; ? movement (0-$3FF) (little endian) + w16(mo_cvt(0)); + //int16 axis6 ; ? movement (0-$3FF) (little endian) + w16(mo_cvt(0)); + //int16 axis7 ; ? movement (0-$3FF) (little endian) + w16(mo_cvt(0)); + //int16 axis8 ; ? movement (0-$3FF) (little endian) + w16(mo_cvt(0)); + + mo_x_delta=0; + mo_y_delta=0; + mo_wheel_delta = 0; + + return MDRS_DataTransfer; + + default: + printf("Mouse: unknown MAPLE COMMAND %d\n", cmd); + return MDRE_UnknownCmd; + } + } +}; + extern u16 kcode[4]; extern s8 joyx[4],joyy[4]; extern u8 rt[4], lt[4]; @@ -1472,6 +1563,10 @@ maple_device* maple_Create(MapleDeviceType type) rv = new maple_keyboard(); break; + case MDT_Mouse: + rv = new maple_mouse(); + break; + case MDT_NaomiJamma: rv = new maple_naomi_jamma(); break; diff --git a/core/hw/maple/maple_devs.h b/core/hw/maple/maple_devs.h index d903163b3..7283522a4 100755 --- a/core/hw/maple/maple_devs.h +++ b/core/hw/maple/maple_devs.h @@ -8,6 +8,7 @@ enum MapleDeviceType MDT_Microphone, MDT_PurupuruPack, MDT_Keyboard, + MDT_Mouse, MDT_NaomiJamma, diff --git a/core/linux-dist/x11.cpp b/core/linux-dist/x11.cpp index fafa5db6d..52f1262cb 100644 --- a/core/linux-dist/x11.cpp +++ b/core/linux-dist/x11.cpp @@ -225,19 +225,85 @@ extern u8 kb_shift; // shift keys pressed (bitmask) extern u8 kb_led; // leds currently lit extern u8 kb_key[6]; // normal keys pressed +extern u32 mo_buttons; +extern f32 mo_x_delta; +extern f32 mo_y_delta; +extern f32 mo_wheel_delta; + +static bool capturing_mouse; +static Cursor empty_cursor = None; + +static Cursor create_empty_cursor() +{ + if (empty_cursor == None) + { + Display *display = (Display*)x11_disp; + char data[] = { 0 }; + + XColor color; + color.red = color.green = color.blue = 0; + + Pixmap pixmap = XCreateBitmapFromData(display, DefaultRootWindow(display), + data, 1, 1); + if (pixmap) + { + empty_cursor = XCreatePixmapCursor(display, pixmap, pixmap, &color, &color, 0, 0); + XFreePixmap(display, pixmap); + } + } + return empty_cursor; +} + +static void destroy_empty_cursor() +{ + if (empty_cursor != None) + { + XFreeCursor((Display*)x11_disp, empty_cursor); + empty_cursor = None; + } +} + +static void x11_capture_mouse() +{ + x11_window_set_text("Reicast - mouse capture"); + capturing_mouse = true; + Cursor cursor = create_empty_cursor(); + Display *display = (Display*)x11_disp; + Window window = (Window)x11_win; + XDefineCursor(display, window, cursor); + XGrabPointer(display, window, False, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask, + GrabModeAsync, GrabModeAsync, None, None, CurrentTime); +} + +static void x11_uncapture_mouse() +{ + x11_window_set_text("Reicast"); + capturing_mouse = false; + Display *display = (Display*)x11_disp; + Window window = (Window)x11_win; + XUndefineCursor(display, window); + XUngrabPointer(display, CurrentTime); +} + void input_x11_handle() { - if (x11_win && x11_keyboard_input) - { - //Handle X11 - XEvent e; + if (!x11_win || (!x11_keyboard_input && !settings.input.DCKeyboard && !settings.input.DCMouse)) + return; - if(XCheckWindowEvent((Display*)x11_disp, (Window)x11_win, KeyPressMask | KeyReleaseMask, &e)) + //Handle X11 + XEvent e; + + while (XCheckWindowEvent((Display*)x11_disp, (Window)x11_win, + KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask + | PointerMotionMask | FocusChangeMask, + &e)) + { + switch(e.type) { - switch(e.type) - { - case KeyPress: - case KeyRelease: + case KeyPress: + case KeyRelease: + { // Dreamcast keyboard emulation if (e.xkey.keycode == KEY_LSHIFT || e.xkey.keycode == KEY_RSHIFT) if (e.type == KeyRelease) @@ -287,66 +353,138 @@ void input_x11_handle() } } - // Normal keyboard handling - if (e.type == KeyRelease && e.xkey.keycode == KEY_ESC) + if (settings.input.DCMouse) { - dc_stop(); + // Start/stop mouse capture with Left Ctrl + Left Alt + if (e.type == KeyPress + && ((e.xkey.keycode == KEY_LALT && (e.xkey.state & ControlMask)) + || (e.xkey.keycode == KEY_LCTRL && (e.xkey.state & Mod1Mask)))) + { + capturing_mouse = !capturing_mouse; + if (capturing_mouse) + x11_capture_mouse(); + else + x11_uncapture_mouse(); + } } + if (x11_keyboard_input) + { + // Normal keyboard handling + if (e.type == KeyRelease && e.xkey.keycode == KEY_ESC + && !(e.xkey.state & (ControlMask | ShiftMask | Mod1Mask))) + { + dc_stop(); + } #ifndef RELEASE - else if (e.xkey.keycode == KEY_F10) - { - // Dump the next frame into a file - dump_frame_switch = e.type == KeyPress; - } + else if (e.xkey.keycode == KEY_F10) + { + // Dump the next frame into a file + dump_frame_switch = e.type == KeyPress; + } #elif FEAT_HAS_NIXPROF - else if (e.type == KeyRelease && e.xkey.keycode == KEY_F10) - { - if (sample_Switch(3000)) { - printf("Starting profiling\n"); - } else { - printf("Stopping profiling\n"); + else if (e.type == KeyRelease && e.xkey.keycode == KEY_F10) + { + if (sample_Switch(3000)) { + printf("Starting profiling\n"); + } else { + printf("Stopping profiling\n"); + } } - } #endif - else if (e.type == KeyRelease && e.xkey.keycode == KEY_F11) - { - x11_fullscreen = !x11_fullscreen; - x11_window_set_fullscreen(x11_fullscreen); - } - else - { - int dc_key = x11_keymap[e.xkey.keycode]; - - if (dc_key == DC_AXIS_LT) + else if (e.type == KeyRelease && e.xkey.keycode == KEY_F11) { - if (e.type == KeyPress) - lt[0] = 255; - else - lt[0] = 0; - } - else if (dc_key == DC_AXIS_RT) - { - if (e.type == KeyPress) - rt[0] = 255; - else - rt[0] = 0; - } - - if (e.type == KeyPress) - { - kcode[0] &= ~dc_key; + x11_fullscreen = !x11_fullscreen; + x11_window_set_fullscreen(x11_fullscreen); } else { - kcode[0] |= dc_key; - } + int dc_key = x11_keymap[e.xkey.keycode]; - #if defined(_DEBUG) - printf("KEY: %d -> %d: %d\n", e.xkey.keycode, dc_key, x11_dc_buttons ); - #endif + if (dc_key == DC_AXIS_LT) + { + if (e.type == KeyPress) + lt[0] = 255; + else + lt[0] = 0; + } + else if (dc_key == DC_AXIS_RT) + { + if (e.type == KeyPress) + rt[0] = 255; + else + rt[0] = 0; + } + + if (e.type == KeyPress) + { + kcode[0] &= ~dc_key; + } + else + { + kcode[0] |= dc_key; + } + + #if defined(_DEBUG) + printf("KEY: %d -> %d: %d\n", e.xkey.keycode, dc_key, x11_dc_buttons ); + #endif + } } - break; - } + } + break; + + case FocusOut: + { + if (capturing_mouse) + x11_uncapture_mouse(); + capturing_mouse = false; + } + break; + + case ButtonPress: + case ButtonRelease: + { + u32 button_mask = 0; + if (e.xbutton.button == Button1) + button_mask = 1 << 2; + else if (e.xbutton.button == Button2) + button_mask = 1 << 1; + + if (button_mask) + { + if (e.type == ButtonPress) + mo_buttons &= ~button_mask; + else + mo_buttons |= button_mask; + } + } + // FALL THROUGH + + case MotionNotify: + { + static int prev_x = -1; + static int prev_y = -1; + + if (settings.input.DCMouse) + { + if (prev_x != -1) + mo_x_delta += (f32)(e.xmotion.x - prev_x) * settings.input.MouseSensitivity / 1000.f; + if (prev_y != -1) + mo_y_delta += (f32)(e.xmotion.y - prev_y) * settings.input.MouseSensitivity / 1000.f; + if (capturing_mouse && (abs(x11_width / 2 - e.xmotion.x) > 10 || abs(x11_height / 2 - e.xmotion.y) > 10)) + { + prev_x = x11_width / 2; + prev_y = x11_height / 2; + XWarpPointer((Display*)x11_disp, None, (Window)x11_win, 0, 0, 0, 0, + prev_x, prev_y); + } + else + { + prev_x = e.xmotion.x; + prev_y = e.xmotion.y; + } + } + } + break; } } } @@ -488,6 +626,8 @@ void x11_window_create() // Add to these for handling other events sWA.event_mask = StructureNotifyMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask; + if (settings.input.DCMouse) + sWA.event_mask |= PointerMotionMask | FocusChangeMask; ui32Mask = CWBackPixel | CWBorderPixel | CWEventMask | CWColormap; x11_width = cfgLoadInt("x11", "width", DEFAULT_WINDOW_WIDTH); diff --git a/core/linux-dist/x11.h b/core/linux-dist/x11.h index 15b8bd9e1..bf1f57323 100644 --- a/core/linux-dist/x11.h +++ b/core/linux-dist/x11.h @@ -4,7 +4,6 @@ extern void* x11_glc; extern void input_x11_init(); extern void event_x11_handle(); extern void input_x11_handle(); -extern void event_x11_handle(); extern void x11_window_create(); extern void x11_window_set_text(const char* text); extern void x11_window_destroy(); @@ -69,6 +68,7 @@ const int KEY_LSHIFT = 50; const int KEY_RSHIFT = 62; const int KEY_LCTRL = 37; const int KEY_RCTRL = 105; +const int KEY_LALT = 64; const int KEY_F1 = 67; const int KEY_F2 = 68; diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 2fad2e750..e8aa22898 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -329,6 +329,10 @@ void LoadSettings() settings.reios.ElfFile = cfgLoadStr("reios", "ElfFile", ""); settings.validate.OpenGlChecks = cfgLoadInt("validate", "OpenGlChecks", 0) != 0; + + settings.input.DCKeyboard = cfgLoadInt("input", "DCKeyboard", 1); + settings.input.DCMouse = cfgLoadInt("input", "DCMouse", 0); + settings.input.MouseSensitivity = cfgLoadInt("input", "MouseSensitivity", 100); #else // TODO Expose this with JNI settings.rend.Clipping = 1; diff --git a/core/types.h b/core/types.h index fa9d2a0f9..003f0f306 100644 --- a/core/types.h +++ b/core/types.h @@ -739,6 +739,12 @@ struct settings_t struct { bool OpenGlChecks; } validate; + + struct { + bool DCKeyboard; + bool DCMouse; + u32 MouseSensitivity; + } input; }; extern settings_t settings;