From 8df6cb316b94b365e803855070c0c6987fb670d3 Mon Sep 17 00:00:00 2001 From: Steve Sims Date: Tue, 17 Oct 2023 14:25:32 +0100 Subject: [PATCH 1/4] preliminary PS/2 mouse support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit experimental hard-coded mouse support. this won’t work properly - mode changes will effectively break things needs some refactoring, and control commands added to allow mouse to be explicitly enabled/disabled and configured --- video/agon.h | 2 ++ video/agon_keyboard.h | 3 ++- video/video.ino | 45 +++++++++++++++++++++++++++++++++++++++++++ video/viewport.h | 11 +++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) diff --git a/video/agon.h b/video/agon.h index 06233d0..db62dd9 100644 --- a/video/agon.h +++ b/video/agon.h @@ -44,6 +44,7 @@ #define VDP_MODE 0x86 // Get screen dimensions #define VDP_RTC 0x87 // RTC #define VDP_KEYSTATE 0x88 // Keyboard repeat rate and LED status +#define VDP_MOUSE 0x89 // Mouse data #define VDP_BUFFERED 0xA0 // Buffered commands #define VDP_LOGICALCOORDS 0xC0 // Switch BBC Micro style logical coords on and off #define VDP_LEGACYMODES 0xC1 // Switch VDP 1.03 compatible modes on and off @@ -62,6 +63,7 @@ #define PACKET_MODE 0x06 // Get screen dimensions #define PACKET_RTC 0x07 // RTC #define PACKET_KEYSTATE 0x08 // Keyboard repeat rate and LED status +#define PACKET_MOUSE 0x09 // Mouse data #define AUDIO_CHANNELS 3 // Default number of audio channels #define MAX_AUDIO_CHANNELS 32 // Maximum number of audio channels diff --git a/video/agon_keyboard.h b/video/agon_keyboard.h index f668c5b..e9421e5 100644 --- a/video/agon_keyboard.h +++ b/video/agon_keyboard.h @@ -20,7 +20,8 @@ inline fabgl::Keyboard* getKeyboard() { // Keyboard setup // void setupKeyboard() { - _PS2Controller.begin(PS2Preset::KeyboardPort0, KbdMode::CreateVirtualKeysQueue); + // _PS2Controller.begin(PS2Preset::KeyboardPort0, KbdMode::CreateVirtualKeysQueue); + _PS2Controller.begin(); auto kb = getKeyboard(); kb->setLayout(&fabgl::UKLayout); kb->setCodePage(fabgl::CodePages::get(1252)); diff --git a/video/video.ino b/video/video.ino index abddac3..c3d227c 100644 --- a/video/video.ino +++ b/video/video.ino @@ -82,6 +82,18 @@ void setup() { copy_font(); set_mode(1); processor->sendModeInformation(); + + // experimental mouse stuff + auto display = _VGAController.get(); + // setup and then terminate absolute positioner - this will set width/height of mouse area for updateAbsolutePosition calls + _PS2Controller.mouse()->setupAbsolutePositioner(canvas->getWidth(), canvas->getHeight(), true, display); + _PS2Controller.mouse()->terminateAbsolutePositioner(); + display->setMouseCursor((CursorName)3); + // _PS2Controller.mouse()->m_area = Size(canvas->getWidth(), canvas->getHeight()); + + // auto mStatus = _PS2Controller.mouse()->status(); + // debug_log("Mouse status: %d, %d, %d\n\r", mStatus.X, mStatus.Y, mStatus.buttons); + boot_screen(); } @@ -103,6 +115,7 @@ void loop() { do_cursor(); } do_keyboard(); + do_mouse(); if (processor->byteAvailable()) { if (cursorState) { @@ -157,6 +170,38 @@ void do_keyboard_terminal() { } } +// Handle the mouse +// +void do_mouse() { + auto mouse = _PS2Controller.mouse(); + // get mouse delta, if the mouse is active + if (mouse->deltaAvailable()) { + MouseDelta delta; + mouse->getNextDelta(&delta, -1); + mouse->updateAbsolutePosition(&delta); + auto mStatus = mouse->status(); + // update mouse cursor position if it's active + _VGAController->setMouseCursorPos(mStatus.X, mStatus.Y); + + auto mousePos = toCurrentCoordinates(mStatus.X, mStatus.Y); + debug_log("Mouse status: %d, %d, d(%d, %d) scaled (%d, %d), scroll: %d (%d), left: %d (%d), right: %d (%d), middle: %d (%d), overflows: %d, %d\n\r", mStatus.X, mStatus.Y, delta.deltaX, delta.deltaY, mousePos.X, mousePos.Y, mStatus.wheelDelta, delta.deltaZ, mStatus.buttons.left, delta.buttons.left, mStatus.buttons.right, delta.buttons.right, mStatus.buttons.middle, delta.buttons.middle, delta.overflowX, delta.overflowY); + + uint8_t packet[] = { + (uint8_t) (mousePos.X & 0xFF), + (uint8_t) ((mousePos.X >> 8) & 0xFF), + (uint8_t) (mousePos.Y & 0xFF), + (uint8_t) ((mousePos.Y >> 8) & 0xFF), + (uint8_t) (mStatus.buttons.left << 0 | mStatus.buttons.right << 1 | mStatus.buttons.middle << 2), + (uint8_t) mStatus.wheelDelta, + (uint8_t) (delta.deltaX & 0xFF), + (uint8_t) ((delta.deltaX >> 8) & 0xFF), + (uint8_t) (delta.deltaY & 0xFF), + (uint8_t) ((delta.deltaY >> 8) & 0xFF), + }; + processor->send_packet(PACKET_MOUSE, sizeof packet, packet); + } +} + // The boot screen // void boot_screen() { diff --git a/video/viewport.h b/video/viewport.h index 3506983..eb3f6ca 100644 --- a/video/viewport.h +++ b/video/viewport.h @@ -68,6 +68,17 @@ Point scale(Point p) { return scale(p.X, p.Y); } +// Convert to currently active coordinate system +// +Point toCurrentCoordinates(int16_t X, int16_t Y) { + // if we're using logical coordinates then we need to scale and invert the Y axis + if (logicalCoords) { + return Point(X * logicalScaleX, ((canvasH - 1) - Y) * logicalScaleY); + } + + return Point(X, Y); +} + // Translate a point relative to the canvas // Point translateCanvas(int16_t X, int16_t Y) { From 5104a2b888e37076d75abcd9bf80762f09bd9bdc Mon Sep 17 00:00:00 2001 From: Steve Sims Date: Thu, 19 Oct 2023 19:45:47 +0100 Subject: [PATCH 2/4] add VDU commands to control mouse --- video/agon.h | 20 ++++ video/{agon_keyboard.h => agon_ps2.h} | 0 video/agon_screen.h | 4 + video/cursor.h | 2 +- video/graphics.h | 3 + video/sprites.h | 62 +++++++++++- video/vdu.h | 3 + video/vdu_sprites.h | 10 ++ video/vdu_stream_processor.h | 40 ++++++++ video/vdu_sys.h | 137 +++++++++++++++++++++++++- video/video.ino | 44 ++------- 11 files changed, 285 insertions(+), 40 deletions(-) rename video/{agon_keyboard.h => agon_ps2.h} (100%) diff --git a/video/agon.h b/video/agon.h index db62dd9..d0cd0bc 100644 --- a/video/agon.h +++ b/video/agon.h @@ -123,6 +123,26 @@ #define AUDIO_STATE_RELEASE 4 // Channel is releasing a note #define AUDIO_STATE_ABORT 5 // Channel is aborting a note +// Mouse commands +#define MOUSE_ENABLE 0 // Enable mouse +#define MOUSE_DISABLE 1 // Disable mouse +#define MOUSE_RESET 2 // Reset mouse +#define MOUSE_SET_CURSOR 3 // Set cursor +#define MOUSE_SET_POSITION 4 // Set mouse position +#define MOUSE_SET_AREA 5 // Set mouse area +#define MOUSE_SET_SAMPLERATE 6 // Set mouse sample rate +#define MOUSE_SET_RESOLUTION 7 // Set mouse resolution +#define MOUSE_SET_SCALING 8 // Set mouse scaling +#define MOUSE_SET_ACCERATION 9 // Set mouse acceleration (1-2000) +#define MOUSE_SET_WHEELACC 10 // Set mouse wheel acceleration + +#define MOUSE_DEFAULT_CURSOR 0; // Default mouse cursor +#define MOUSE_DEFAULT_SAMPLERATE 60; // Default mouse sample rate +#define MOUSE_DEFAULT_RESOLUTION 2; // Default mouse resolution (4 counts/mm) +#define MOUSE_DEFAULT_SCALING 1; // Default mouse scaling (1:1) +#define MOUSE_DEFAULT_ACCELERATION 180; // Default mouse acceleration +#define MOUSE_DEFAULT_WHEELACC 60000; // Default mouse wheel acceleration + // Buffered commands #define BUFFERED_WRITE 0x00 // Write to a numbered buffer #define BUFFERED_CALL 0x01 // Call buffered commands diff --git a/video/agon_keyboard.h b/video/agon_ps2.h similarity index 100% rename from video/agon_keyboard.h rename to video/agon_ps2.h diff --git a/video/agon_screen.h b/video/agon_screen.h index bd6b485..dec196b 100644 --- a/video/agon_screen.h +++ b/video/agon_screen.h @@ -148,4 +148,8 @@ void switchBuffer() { } } +void setMouseCursorPos(uint16_t x, uint16_t y) { + _VGAController->setMouseCursorPos(x, y); +} + #endif // AGON_SCREEN_H diff --git a/video/cursor.h b/video/cursor.h index d7b2c55..188946a 100644 --- a/video/cursor.h +++ b/video/cursor.h @@ -3,7 +3,7 @@ #include -#include "agon_keyboard.h" +#include "agon_ps2.h" #include "graphics.h" #include "viewport.h" // TODO remove this, somehow diff --git a/video/graphics.h b/video/graphics.h index f970f22..f19b92f 100644 --- a/video/graphics.h +++ b/video/graphics.h @@ -4,6 +4,7 @@ #include #include "agon.h" +#include "agon_ps2.h" #include "agon_screen.h" #include "agon_fonts.h" // The Acorn BBC Micro Font #include "agon_palette.h" // Colour lookup table @@ -643,6 +644,8 @@ int8_t change_mode(uint8_t mode) { switchBuffer(); cls(false); } + resetMousePositioner(canvasW, canvasH, _VGAController.get()); + setMouseCursor(); debug_log("do_modeChange: canvas(%d,%d), scale(%f,%f), mode %d, videoMode %d\n\r", canvasW, canvasH, logicalScaleX, logicalScaleY, mode, videoMode); return 0; } diff --git a/video/sprites.h b/video/sprites.h index 4808d6d..5bebd02 100644 --- a/video/sprites.h +++ b/video/sprites.h @@ -1,12 +1,16 @@ #ifndef SPRITES_H #define SPRITES_H +#include +#include #include -#include +#include #include +#include #include #include "agon.h" +#include "agon_ps2.h" #include "agon_screen.h" uint16_t currentBitmap = BUFFERED_BITMAP_BASEID; // Current bitmap ID @@ -18,6 +22,9 @@ Sprite sprites[MAX_SPRITES]; // Sprite object storage // track which sprites may be using a bitmap std::unordered_map> bitmapUsers; +std::unordered_map cursors; // Storage for our cursors +uint16_t mCursor = MOUSE_DEFAULT_CURSOR; // Mouse cursor + std::shared_ptr getBitmap(uint16_t id = currentBitmap) { if (bitmaps.find(id) != bitmaps.end()) { return bitmaps[id]; @@ -44,9 +51,15 @@ void drawBitmap(uint16_t x, uint16_t y) { } void resetBitmaps() { + // if we're using a bitmap as a cursor then the cursor needs to change too + if (cursors.find(currentBitmap) != cursors.end()) { + uint16_t cursor = MOUSE_DEFAULT_CURSOR; + setMouseCursor(cursor); + } bitmaps.clear(); // this will only be used after resetting sprites, so we can clear the bitmapUsers list bitmapUsers.clear(); + cursors.clear(); setCurrentBitmap(BUFFERED_BITMAP_BASEID); } @@ -193,4 +206,51 @@ void resetSprites() { waitPlotCompletion(); } +void clearCursor(uint16_t cursor) { + if (cursors.find(cursor) != cursors.end()) { + cursors.erase(cursor); + } +} + +bool makeCursor(uint16_t bitmapId, uint16_t hotX, uint16_t hotY) { + auto bitmap = getBitmap(bitmapId); + if (!bitmap) { + debug_log("addCursor: bitmap %d not found\n\r", bitmapId); + return false; + } + fabgl::Cursor c; + c.bitmap = *bitmap; + c.hotspotX = std::min(static_cast(std::max(static_cast(hotX), 0)), static_cast(bitmap->width - 1)); + c.hotspotY = std::min(static_cast(std::max(static_cast(hotY), 0)), static_cast(bitmap->height - 1)); + cursors[bitmapId] = c; + return true; +} + +bool setMouseCursor(uint16_t cursor = mCursor) { + // if our mouse is enabled, then we'll set the cursor + if (mouseEnabled) { + // if this cursor exists then set it + // first, check whether it's a built-in cursor + auto minValue = static_cast(std::numeric_limits::type>::min()); + auto maxValue = static_cast(std::numeric_limits::type>::max()); + if (minValue <= cursor && cursor <= maxValue) { + _VGAController->setMouseCursor(static_cast(cursor)); + mCursor = cursor; + return true; + } + // otherwise, check whether it's a custom cursor + if (cursors.find(cursor) != cursors.end()) { + _VGAController->setMouseCursor(&cursors[cursor]); + mCursor = cursor; + return true; + } + } + // otherwise make sure we remove the cursor and keep track of the requested cursor number + _VGAController->setMouseCursor(nullptr); + if (cursor != 65535) { + mCursor = cursor; + } + return false; +} + #endif // SPRITES_H diff --git a/video/vdu.h b/video/vdu.h index f774ea0..3dc6b1e 100644 --- a/video/vdu.h +++ b/video/vdu.h @@ -136,6 +136,9 @@ void VDUStreamProcessor::vdu_mode() { if (mode >= 0) { set_mode(mode); sendModeInformation(); + if (mouseEnabled) { + sendMouseData(); + } } } diff --git a/video/vdu_sprites.h b/video/vdu_sprites.h index f390a5b..755b64a 100644 --- a/video/vdu_sprites.h +++ b/video/vdu_sprites.h @@ -158,6 +158,16 @@ void VDUStreamProcessor::vdu_sys_sprites(void) { debug_log("vdu_sys_sprites: sprite %d - bitmap %d added as frame %d\n\r", getCurrentSprite(), bufferId, getSprite()->framesCount-1); } break; + case 0x40: { // Setup mouse cursor from current bitmap + auto hotX = readByte_t(); if (hotX == -1) return; + auto hotY = readByte_t(); if (hotY == -1) return; + if (makeCursor(getCurrentBitmapId(), hotX, hotY)) { + debug_log("vdu_sys_sprites: cursor created from bitmap %d\n\r", getCurrentBitmapId()); + } else { + debug_log("vdu_sys_sprites: cursor failed to create from bitmap %d\n\r", getCurrentBitmapId()); + } + } break; + default: { debug_log("vdu_sys_sprites: unknown command %d\n\r", cmd); } break; diff --git a/video/vdu_stream_processor.h b/video/vdu_stream_processor.h index 1a67f0b..3cafbdb 100644 --- a/video/vdu_stream_processor.h +++ b/video/vdu_stream_processor.h @@ -5,8 +5,10 @@ #include #include "agon.h" +#include "agon_ps2.h" #include "buffer_stream.h" #include "types.h" +#include "viewport.h" class VDUStreamProcessor { private: @@ -43,6 +45,7 @@ class VDUStreamProcessor { void vdu_sys_video_time(); void sendKeyboardState(); void vdu_sys_keystate(); + void vdu_sys_mouse(); void vdu_sys_scroll(); void vdu_sys_cursorBehaviour(); void vdu_sys_udg(char c); @@ -103,6 +106,8 @@ class VDUStreamProcessor { } void send_packet(uint8_t code, uint16_t len, uint8_t data[]); + void sendMouseData(MouseDelta * delta); + void processAllAvailable(); void processNext(); @@ -235,6 +240,41 @@ void VDUStreamProcessor::send_packet(uint8_t code, uint16_t len, uint8_t data[]) } } +void VDUStreamProcessor::sendMouseData(MouseDelta * delta = nullptr) { + auto mouse = getMouse(); + uint16_t mouseX = 0; + uint16_t mouseY = 0; + uint8_t buttons = 0; + uint8_t wheelDelta = 0; + uint16_t deltaX = 0; + uint16_t deltaY = 0; + if (delta) { + deltaX = delta->deltaX; + deltaY = delta->deltaY; + } + if (mouse) { + auto mStatus = mouse->status(); + auto mousePos = toCurrentCoordinates(mStatus.X, mStatus.Y); + mouseX = mousePos.X; + mouseY = mousePos.Y; + buttons = mStatus.buttons.left << 0 | mStatus.buttons.right << 1 | mStatus.buttons.middle << 2; + wheelDelta = mStatus.wheelDelta; + } + debug_log("sendMouseData: %d %d %d %d %d %d %d %d %d %d\n\r", mouseX, mouseY, buttons, wheelDelta, deltaX, deltaY); + uint8_t packet[] = { + (uint8_t) (mouseX & 0xFF), + (uint8_t) ((mouseX >> 8) & 0xFF), + (uint8_t) (mouseY & 0xFF), + (uint8_t) ((mouseY >> 8) & 0xFF), + (uint8_t) buttons, + (uint8_t) wheelDelta, + (uint8_t) (deltaX & 0xFF), + (uint8_t) ((deltaX >> 8) & 0xFF), + (uint8_t) (deltaY & 0xFF), + (uint8_t) ((deltaY >> 8) & 0xFF), + }; + send_packet(PACKET_MOUSE, sizeof packet, packet); +} // Process all available commands from the stream // void VDUStreamProcessor::processAllAvailable() { diff --git a/video/vdu_sys.h b/video/vdu_sys.h index f84024e..5a72900 100644 --- a/video/vdu_sys.h +++ b/video/vdu_sys.h @@ -1,11 +1,13 @@ #ifndef VDU_SYS_H #define VDU_SYS_H +#include + #include #include #include "agon.h" -#include "agon_keyboard.h" +#include "agon_ps2.h" #include "cursor.h" #include "graphics.h" #include "vdu_audio.h" @@ -136,6 +138,9 @@ void VDUStreamProcessor::vdu_sys_video() { case VDP_KEYSTATE: { // VDU 23, 0, &88, repeatRate; repeatDelay; status vdu_sys_keystate(); } break; + case VDP_MOUSE: { // VDU 23, 0, &89, command, + vdu_sys_mouse(); + } break; case VDP_BUFFERED: { // VDU 23, 0, &A0, bufferId; command, vdu_sys_buffered(); } break; @@ -305,6 +310,136 @@ void VDUStreamProcessor::vdu_sys_keystate() { sendKeyboardState(); } +// VDU 23, 0, &89, command, []: Handle mouse requests +// +void VDUStreamProcessor::vdu_sys_mouse() { + auto command = readByte_t(); if (command == -1) return; + + switch (command) { + case MOUSE_ENABLE: { + // ensure mouse is enabled, enabling its port if necessary + if (enableMouse()) { + // mouse can be enabled, so set cursor + if (!setMouseCursor()) { + uint16_t cursor = MOUSE_DEFAULT_CURSOR; + setMouseCursor(cursor); + } + debug_log("vdu_sys_mouse: mouse enabled\n\r"); + } else { + debug_log("vdu_sys_mouse: mouse enable failed\n\r"); + } + // send mouse data (with no delta) to indicate command processed successfully + sendMouseData(); + } break; + + case MOUSE_DISABLE: { + if (disableMouse()) { + setMouseCursor(65535); // set cursor to be a non-existant cursor + debug_log("vdu_sys_mouse: mouse disabled\n\r"); + } else { + debug_log("vdu_sys_mouse: mouse disable failed\n\r"); + } + sendMouseData(); + } break; + + case MOUSE_RESET: { + debug_log("vdu_sys_mouse: reset mouse\n\r"); + // call the reset for the mouse + if (resetMouse()) { + // mouse successfully reset, so set cursor + if (!setMouseCursor()) { + uint16_t cursor = MOUSE_DEFAULT_CURSOR; + setMouseCursor(cursor); + } + } + sendMouseData(); + } break; + + case MOUSE_SET_CURSOR: { + auto cursor = readWord_t(); if (cursor == -1) return; + if (setMouseCursor(cursor)) { + sendMouseData(); + } + debug_log("vdu_sys_mouse: set cursor\n\r"); + } break; + + case MOUSE_SET_POSITION: { + auto x = readWord_t(); if (x == -1) return; + auto y = readWord_t(); if (y == -1) return; + // normalise coordinates + auto p = translateViewport(scale(x, y)); + + // need to update position in mouse status + setMousePos(p.X, p.Y); + setMouseCursorPos(p.X, p.Y); + + sendMouseData(); + debug_log("vdu_sys_mouse: set position\n\r"); + } break; + + case MOUSE_SET_AREA: { + auto x = readWord_t(); if (x == -1) return; + auto y = readWord_t(); if (y == -1) return; + auto x2 = readWord_t(); if (x2 == -1) return; + auto y2 = readWord_t(); if (y2 == -1) return; + + debug_log("vdu_sys_mouse: set area can't be properly supported with current fab-gl\n\r"); + // TODO set area to width/height using bottom/right only + } break; + + case MOUSE_SET_SAMPLERATE: { + auto rate = readByte_t(); if (rate == -1) return; + if (setMouseSampleRate(rate)) { + debug_log("vdu_sys_mouse: set sample rate %d\n\r", rate); + // success so send new data packet (triggering VDP flag) + sendMouseData(); + } + debug_log("vdu_sys_mouse: set sample rate %d failed\n\r", rate); + } break; + + case MOUSE_SET_RESOLUTION: { + auto resolution = readByte_t(); if (resolution == -1) return; + if (setMouseResolution(resolution)) { + // success so send new data packet (triggering VDP flag) + sendMouseData(); + debug_log("vdu_sys_mouse: set resolution %d\n\r", resolution); + return; + } + debug_log("vdu_sys_mouse: set resolution %d failed\n\r", resolution); + } break; + + case MOUSE_SET_SCALING: { + auto scaling = readByte_t(); if (scaling == -1) return; + if (setMouseScaling(scaling)) { + // success so send new data packet (triggering VDP flag) + sendMouseData(); + debug_log("vdu_sys_mouse: set scaling %d\n\r", scaling); + return; + } + } break; + + case MOUSE_SET_ACCERATION: { + auto acceleration = readWord_t(); if (acceleration == -1) return; + if (setMouseAcceleration(acceleration)) { + // success so send new data packet (triggering VDP flag) + sendMouseData(); + debug_log("vdu_sys_mouse: set acceleration %d\n\r", acceleration); + return; + } + } break; + + case MOUSE_SET_WHEELACC: { + auto wheelAcc = read24_t(); if (wheelAcc == -1) return; + if (setMouseWheelAcceleration(wheelAcc)) { + // success so send new data packet (triggering VDP flag) + sendMouseData(); + debug_log("vdu_sys_mouse: set wheel acceleration %d\n\r", wheelAcc); + return; + } + } break; + } +} + // VDU 23,7: Scroll rectangle on screen // void VDUStreamProcessor::vdu_sys_scroll() { diff --git a/video/video.ino b/video/video.ino index c3d227c..f985374 100644 --- a/video/video.ino +++ b/video/video.ino @@ -56,7 +56,7 @@ #define SERIALBAUDRATE 115200 #include "agon.h" // Configuration file -#include "agon_keyboard.h" // Keyboard support +#include "agon_ps2.h" // Keyboard support #include "agon_audio.h" // Audio support #include "graphics.h" // Graphics support #include "cursor.h" // Cursor support @@ -77,23 +77,11 @@ void setup() { setupVDPProtocol(); processor = new VDUStreamProcessor(&VDPSerial); processor->wait_eZ80(); - setupKeyboard(); + setupKeyboardAndMouse(); init_audio(); copy_font(); set_mode(1); processor->sendModeInformation(); - - // experimental mouse stuff - auto display = _VGAController.get(); - // setup and then terminate absolute positioner - this will set width/height of mouse area for updateAbsolutePosition calls - _PS2Controller.mouse()->setupAbsolutePositioner(canvas->getWidth(), canvas->getHeight(), true, display); - _PS2Controller.mouse()->terminateAbsolutePositioner(); - display->setMouseCursor((CursorName)3); - // _PS2Controller.mouse()->m_area = Size(canvas->getWidth(), canvas->getHeight()); - - // auto mStatus = _PS2Controller.mouse()->status(); - // debug_log("Mouse status: %d, %d, %d\n\r", mStatus.X, mStatus.Y, mStatus.buttons); - boot_screen(); } @@ -173,32 +161,14 @@ void do_keyboard_terminal() { // Handle the mouse // void do_mouse() { - auto mouse = _PS2Controller.mouse(); // get mouse delta, if the mouse is active - if (mouse->deltaAvailable()) { - MouseDelta delta; - mouse->getNextDelta(&delta, -1); - mouse->updateAbsolutePosition(&delta); + MouseDelta delta; + if (mouseMoved(&delta)) { + auto mouse = getMouse(); auto mStatus = mouse->status(); // update mouse cursor position if it's active - _VGAController->setMouseCursorPos(mStatus.X, mStatus.Y); - - auto mousePos = toCurrentCoordinates(mStatus.X, mStatus.Y); - debug_log("Mouse status: %d, %d, d(%d, %d) scaled (%d, %d), scroll: %d (%d), left: %d (%d), right: %d (%d), middle: %d (%d), overflows: %d, %d\n\r", mStatus.X, mStatus.Y, delta.deltaX, delta.deltaY, mousePos.X, mousePos.Y, mStatus.wheelDelta, delta.deltaZ, mStatus.buttons.left, delta.buttons.left, mStatus.buttons.right, delta.buttons.right, mStatus.buttons.middle, delta.buttons.middle, delta.overflowX, delta.overflowY); - - uint8_t packet[] = { - (uint8_t) (mousePos.X & 0xFF), - (uint8_t) ((mousePos.X >> 8) & 0xFF), - (uint8_t) (mousePos.Y & 0xFF), - (uint8_t) ((mousePos.Y >> 8) & 0xFF), - (uint8_t) (mStatus.buttons.left << 0 | mStatus.buttons.right << 1 | mStatus.buttons.middle << 2), - (uint8_t) mStatus.wheelDelta, - (uint8_t) (delta.deltaX & 0xFF), - (uint8_t) ((delta.deltaX >> 8) & 0xFF), - (uint8_t) (delta.deltaY & 0xFF), - (uint8_t) ((delta.deltaY >> 8) & 0xFF), - }; - processor->send_packet(PACKET_MOUSE, sizeof packet, packet); + setMouseCursorPos(mStatus.X, mStatus.Y); + processor->sendMouseData(&delta); } } From e5032c651b33627d01d7ec776747ea0b9ebf400f Mon Sep 17 00:00:00 2001 From: Steve Sims Date: Thu, 19 Oct 2023 19:46:05 +0100 Subject: [PATCH 3/4] mouse implementation --- video/agon_ps2.h | 193 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 187 insertions(+), 6 deletions(-) diff --git a/video/agon_ps2.h b/video/agon_ps2.h index e9421e5..27e5482 100644 --- a/video/agon_ps2.h +++ b/video/agon_ps2.h @@ -1,5 +1,7 @@ -#ifndef AGON_KEYBOARD_H -#define AGON_KEYBOARD_H +#ifndef AGON_PS2_H +#define AGON_PS2_H + +#include #include @@ -12,15 +14,26 @@ uint8_t _modifiers = 0; // Last pressed key modifiers uint16_t kbRepeatDelay = 500; // Keyboard repeat delay ms (250, 500, 750 or 1000) uint16_t kbRepeatRate = 100; // Keyboard repeat rate ms (between 33 and 500) +bool mouseEnabled = false; // Mouse enabled +uint8_t mSampleRate = MOUSE_DEFAULT_SAMPLERATE; // Mouse sample rate +uint8_t mResolution = MOUSE_DEFAULT_RESOLUTION; // Mouse resolution +uint8_t mScaling = MOUSE_DEFAULT_SCALING; // Mouse scaling +uint16_t mAcceleration = MOUSE_DEFAULT_ACCELERATION; // Mouse acceleration +uint32_t mWheelAcc = MOUSE_DEFAULT_WHEELACC; // Mouse wheel acceleration + // Get keyboard instance inline fabgl::Keyboard* getKeyboard() { return _PS2Controller.keyboard(); } -// Keyboard setup +// Get mouse instance +inline fabgl::Mouse* getMouse() { + return _PS2Controller.mouse(); +} + +// Keyboard and mouse setup // -void setupKeyboard() { - // _PS2Controller.begin(PS2Preset::KeyboardPort0, KbdMode::CreateVirtualKeysQueue); +void setupKeyboardAndMouse() { _PS2Controller.begin(); auto kb = getKeyboard(); kb->setLayout(&fabgl::UKLayout); @@ -179,4 +192,172 @@ void setKeyboardState(uint16_t delay, uint16_t rate, uint8_t ledState) { kb->setTypematicRateAndDelay(kbRepeatRate, kbRepeatDelay); } -#endif // AGON_KEYBOARD_H +bool enableMouse() { + if (mouseEnabled) { + return true; + } + auto mouse = getMouse(); + if (!mouse) { + return false; + } + mouse->resumePort(); + mouseEnabled = true; + return true; +} + +bool disableMouse() { + if (!mouseEnabled) { + return true; + } + auto mouse = getMouse(); + if (!mouse) { + return false; + } + mouse->suspendPort(); + mouseEnabled = false; + return true; +} + +bool setMouseSampleRate(uint8_t rate) { + auto mouse = getMouse(); + if (!mouse) { + return false; + } + // sampleRate: valid values are 10, 20, 40, 60, 80, 100, and 200 (samples/sec) + if (rate == 0) rate = MOUSE_DEFAULT_SAMPLERATE; + auto rates = std::vector{ 10, 20, 40, 60, 80, 100, 200 }; + if (std::find(rates.begin(), rates.end(), rate) == rates.end()) { + debug_log("vdu_sys_mouse: invalid sample rate %d\n\r", rate); + return false; + } + + if (mouse->setSampleRate(rate)) { + mSampleRate = rate; + return true; + } + return false; +} + +bool setMouseResolution(int8_t resolution) { + auto mouse = getMouse(); + if (!mouse) { + return false; + } + if (resolution == -1) resolution = MOUSE_DEFAULT_RESOLUTION; + if (resolution > 3) { + debug_log("setMouseResolution: invalid resolution %d\n\r", resolution); + return false; + } + + if (mouse->setResolution(resolution)) { + mResolution = resolution; + return true; + } + return false; +} + +bool setMouseScaling(uint8_t scaling) { + auto mouse = getMouse(); + if (!mouse) { + return false; + } + if (scaling == 0) scaling = MOUSE_DEFAULT_SCALING; + if (scaling > 2) { + debug_log("setMouseScaling: invalid scaling %d\n\r", scaling); + return false; + } + + if (mouse->setScaling(scaling)) { + mScaling = scaling; + return true; + } + return false; +} + +bool setMouseAcceleration(uint16_t acceleration) { + auto mouse = getMouse(); + if (!mouse) { + return false; + } + if (acceleration == 0) acceleration = MOUSE_DEFAULT_ACCELERATION; + + // get current acceleration + auto & currentAcceleration = mouse->movementAcceleration(); + + // change the acceleration + currentAcceleration = acceleration; + + return false; +} + +bool setMouseWheelAcceleration(uint32_t acceleration) { + auto mouse = getMouse(); + if (!mouse) { + return false; + } + if (acceleration == 0) acceleration = MOUSE_DEFAULT_WHEELACC; + + // get current acceleration + auto & currentAcceleration = mouse->wheelAcceleration(); + + // change the acceleration + currentAcceleration = acceleration; + + return false; +} + +bool resetMousePositioner(uint16_t width, uint16_t height, fabgl::VGABaseController * display) { + auto mouse = getMouse(); + if (!mouse) { + return false; + } + // setup and then terminate absolute positioner + // this will set width/height of mouse area for updateAbsolutePosition calls + mouse->setupAbsolutePositioner(width, height, true, display); + mouse->terminateAbsolutePositioner(); + return true; +} + +bool setMousePos(uint16_t x, uint16_t y) { + auto mouse = getMouse(); + if (!mouse) { + return false; + } + auto & status = mouse->status(); + status.X = x; + status.Y = y; + return true; +} + +bool resetMouse() { + auto mouse = getMouse(); + if (!mouse) { + return false; + } + // reset parameters for mouse + // set default sample rate, resolution, scaling, acceleration, wheel acceleration + setMouseSampleRate(0); + setMouseResolution(-1); + setMouseScaling(0); + setMouseAcceleration(0); + setMouseWheelAcceleration(0); + return mouse->reset(); +} + +bool mouseMoved(MouseDelta * delta) { + if (!mouseEnabled) { + return false; + } + auto mouse = getMouse(); + if (!mouse) { + return false; + } + if (mouse->deltaAvailable()) { + mouse->getNextDelta(delta, -1); + mouse->updateAbsolutePosition(delta); + return true; + } + return false; +} + +#endif // AGON_PS2_H From 2ea777df1d0b1df91016c4c2693ed172ed805696 Mon Sep 17 00:00:00 2001 From: Steve Sims Date: Mon, 23 Oct 2023 18:16:11 +0100 Subject: [PATCH 4/4] only enable mouse if one is available also fix a compilation issue --- video/agon_ps2.h | 12 +++++-- video/sprites.h | 94 ++++++++++++++++++++++++------------------------ 2 files changed, 57 insertions(+), 49 deletions(-) diff --git a/video/agon_ps2.h b/video/agon_ps2.h index 27e5482..7c414e1 100644 --- a/video/agon_ps2.h +++ b/video/agon_ps2.h @@ -192,6 +192,15 @@ void setKeyboardState(uint16_t delay, uint16_t rate, uint8_t ledState) { kb->setTypematicRateAndDelay(kbRepeatRate, kbRepeatDelay); } +bool updateMouseEnabled() { + auto mouse = getMouse(); + if (!mouse) { + return false; + } + mouseEnabled = mouse->isMouseAvailable(); + return mouseEnabled; +} + bool enableMouse() { if (mouseEnabled) { return true; @@ -201,8 +210,7 @@ bool enableMouse() { return false; } mouse->resumePort(); - mouseEnabled = true; - return true; + return updateMouseEnabled(); } bool disableMouse() { diff --git a/video/sprites.h b/video/sprites.h index 5bebd02..2601546 100644 --- a/video/sprites.h +++ b/video/sprites.h @@ -50,6 +50,53 @@ void drawBitmap(uint16_t x, uint16_t y) { } } +void clearCursor(uint16_t cursor) { + if (cursors.find(cursor) != cursors.end()) { + cursors.erase(cursor); + } +} + +bool makeCursor(uint16_t bitmapId, uint16_t hotX, uint16_t hotY) { + auto bitmap = getBitmap(bitmapId); + if (!bitmap) { + debug_log("addCursor: bitmap %d not found\n\r", bitmapId); + return false; + } + fabgl::Cursor c; + c.bitmap = *bitmap; + c.hotspotX = std::min(static_cast(std::max(static_cast(hotX), 0)), static_cast(bitmap->width - 1)); + c.hotspotY = std::min(static_cast(std::max(static_cast(hotY), 0)), static_cast(bitmap->height - 1)); + cursors[bitmapId] = c; + return true; +} + +bool setMouseCursor(uint16_t cursor = mCursor) { + // if our mouse is enabled, then we'll set the cursor + if (mouseEnabled) { + // if this cursor exists then set it + // first, check whether it's a built-in cursor + auto minValue = static_cast(std::numeric_limits::type>::min()); + auto maxValue = static_cast(std::numeric_limits::type>::max()); + if (minValue <= cursor && cursor <= maxValue) { + _VGAController->setMouseCursor(static_cast(cursor)); + mCursor = cursor; + return true; + } + // otherwise, check whether it's a custom cursor + if (cursors.find(cursor) != cursors.end()) { + _VGAController->setMouseCursor(&cursors[cursor]); + mCursor = cursor; + return true; + } + } + // otherwise make sure we remove the cursor and keep track of the requested cursor number + _VGAController->setMouseCursor(nullptr); + if (cursor != 65535) { + mCursor = cursor; + } + return false; +} + void resetBitmaps() { // if we're using a bitmap as a cursor then the cursor needs to change too if (cursors.find(currentBitmap) != cursors.end()) { @@ -206,51 +253,4 @@ void resetSprites() { waitPlotCompletion(); } -void clearCursor(uint16_t cursor) { - if (cursors.find(cursor) != cursors.end()) { - cursors.erase(cursor); - } -} - -bool makeCursor(uint16_t bitmapId, uint16_t hotX, uint16_t hotY) { - auto bitmap = getBitmap(bitmapId); - if (!bitmap) { - debug_log("addCursor: bitmap %d not found\n\r", bitmapId); - return false; - } - fabgl::Cursor c; - c.bitmap = *bitmap; - c.hotspotX = std::min(static_cast(std::max(static_cast(hotX), 0)), static_cast(bitmap->width - 1)); - c.hotspotY = std::min(static_cast(std::max(static_cast(hotY), 0)), static_cast(bitmap->height - 1)); - cursors[bitmapId] = c; - return true; -} - -bool setMouseCursor(uint16_t cursor = mCursor) { - // if our mouse is enabled, then we'll set the cursor - if (mouseEnabled) { - // if this cursor exists then set it - // first, check whether it's a built-in cursor - auto minValue = static_cast(std::numeric_limits::type>::min()); - auto maxValue = static_cast(std::numeric_limits::type>::max()); - if (minValue <= cursor && cursor <= maxValue) { - _VGAController->setMouseCursor(static_cast(cursor)); - mCursor = cursor; - return true; - } - // otherwise, check whether it's a custom cursor - if (cursors.find(cursor) != cursors.end()) { - _VGAController->setMouseCursor(&cursors[cursor]); - mCursor = cursor; - return true; - } - } - // otherwise make sure we remove the cursor and keep track of the requested cursor number - _VGAController->setMouseCursor(nullptr); - if (cursor != 65535) { - mCursor = cursor; - } - return false; -} - #endif // SPRITES_H