From 5cb9b85396d9cf8f710e64be033a8e961dc383f2 Mon Sep 17 00:00:00 2001 From: elasota Date: Tue, 24 Dec 2019 18:39:30 -0500 Subject: [PATCH] Fix house data corruption, progress to first screen --- .gitignore | 1 + GpApp/Banner.cpp | 8 +- GpApp/GameOver.cpp | 2 + GpApp/GliderStructs.h | 4 + GpApp/HouseIO.cpp | 41 +- GpApp/Play.cpp | 3 - GpApp/Render.cpp | 7 +- GpApp/Transitions.cpp | 6 +- GpApp/Utilities.cpp | 7 +- GpCommon/GpPixelFormat.h | 1 + PortabilityLayer/MenuManager.cpp | 543 ++++++++++++++++-- PortabilityLayer/MenuManager.h | 10 +- PortabilityLayer/PLCore.cpp | 101 +++- PortabilityLayer/PLCore.h | 5 +- PortabilityLayer/PLEventQueue.cpp | 13 +- PortabilityLayer/PLEventQueue.h | 5 +- PortabilityLayer/PLMenus.cpp | 2 +- PortabilityLayer/PLQDOffscreen.cpp | 3 +- PortabilityLayer/PLQDOffscreen.h | 2 +- PortabilityLayer/PLQuickdraw.cpp | 123 +++- PortabilityLayer/PLQuickdraw.h | 1 + PortabilityLayer/PortabilityLayer.vcxproj | 5 + .../PortabilityLayer.vcxproj.filters | 5 +- PortabilityLayer/QDManager.cpp | 29 +- PortabilityLayer/QDManager.h | 4 + PortabilityLayer/QDPixMap.cpp | 5 +- PortabilityLayer/QDStandardPalette.cpp | 4 +- PortabilityLayer/WindowManager.cpp | 57 ++ PortabilityLayer/WindowManager.h | 3 + README.txt | 9 + 30 files changed, 896 insertions(+), 113 deletions(-) diff --git a/.gitignore b/.gitignore index a5a7938..0ae5a72 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ *.res .vs/* Packaged/* +DebugData/* \ No newline at end of file diff --git a/GpApp/Banner.cpp b/GpApp/Banner.cpp index 9a393d8..aeac628 100644 --- a/GpApp/Banner.cpp +++ b/GpApp/Banner.cpp @@ -72,13 +72,13 @@ void DrawBanner (Point *topLeft) theErr = CreateOffScreenGWorld(&tempMask, &mapBounds, 1); SetGWorld(tempMask, nil); LoadGraphic(kBannerPageBottomMask); - + CopyMask((BitMap *)*GetGWorldPixMap(tempMap), (BitMap *)*GetGWorldPixMap(tempMask), (BitMap *)*GetGWorldPixMap(workSrcMap), &mapBounds, &mapBounds, &partPage); SetPort((GrafPtr)workSrcMap); - + SetGWorld(wasCPort, wasWorld); DisposeGWorld(tempMap); DisposeGWorld(tempMask); @@ -176,6 +176,9 @@ void BringUpBanner (void) DrawBanner(&topLeft); DrawBannerMessage(topLeft); + + DumpScreenOn(&justRoomsRect); + // if (quickerTransitions) // DissBitsChunky(&justRoomsRect); // was workSrcRect // else @@ -186,6 +189,7 @@ void BringUpBanner (void) CopyBits((BitMap *)*GetGWorldPixMap(backSrcMap), (BitMap *)*GetGWorldPixMap(workSrcMap), &wholePage, &wholePage, srcCopy, nil); + if (demoGoing) WaitForInputEvent(4); diff --git a/GpApp/GameOver.cpp b/GpApp/GameOver.cpp index d553cc2..db67bd8 100644 --- a/GpApp/GameOver.cpp +++ b/GpApp/GameOver.cpp @@ -474,6 +474,8 @@ void DoDiedGameOver (void) pagesStuck = 8; userAborted = true; } + + Delay(1, nullptr); } while (TickCount() < nextLoop); nextLoop = TickCount() + 2; diff --git a/GpApp/GliderStructs.h b/GpApp/GliderStructs.h index e3f69a2..6df0176 100644 --- a/GpApp/GliderStructs.h +++ b/GpApp/GliderStructs.h @@ -194,7 +194,11 @@ typedef struct Boolean unusedBoolean; // 1 int16_t firstRoom; // 2 int16_t nRooms; // 2 + + int16_t padding; roomType rooms[1]; // 348 * nRooms + + static const size_t kBinaryDataSize = 866; } houseType, *housePtr, **houseHand; // total = 866 + typedef struct diff --git a/GpApp/HouseIO.cpp b/GpApp/HouseIO.cpp index ddc9821..00f9844 100644 --- a/GpApp/HouseIO.cpp +++ b/GpApp/HouseIO.cpp @@ -598,7 +598,7 @@ bool ByteSwapHouse(housePtr house, size_t sizeInBytes) PortabilityLayer::ByteSwap::BigInt16(house->firstRoom); PortabilityLayer::ByteSwap::BigInt16(house->nRooms); - const size_t roomDataSize = sizeInBytes + sizeof(roomType) - sizeof(houseType); + const size_t roomDataSize = sizeInBytes - houseType::kBinaryDataSize; if (house->nRooms < 1 || roomDataSize / sizeof(roomType) < static_cast(house->nRooms)) return false; @@ -614,6 +614,9 @@ Boolean ReadHouse (void) long byteCount; OSErr theErr; short whichRoom; + + // There should be no padding remaining the house type + PL_STATIC_ASSERT(sizeof(houseType) - sizeof(roomType) == houseType::kBinaryDataSize + 2); if (!houseOpen) { @@ -649,8 +652,11 @@ Boolean ReadHouse (void) if (thisHouse != nil) DisposeHandle((Handle)thisHouse); + + // GP: Correct for padding + const size_t alignmentPadding = sizeof(houseType) - sizeof(roomType) - houseType::kBinaryDataSize; - thisHouse = (houseHand)NewHandle(byteCount); + thisHouse = (houseHand)NewHandle(byteCount + alignmentPadding); if (thisHouse == nil) { YellowAlert(kYellowNoMemory, 10); @@ -666,14 +672,24 @@ Boolean ReadHouse (void) } HLock((Handle)thisHouse); - theErr = FSRead(houseRefNum, &byteCount, *thisHouse); - if (theErr != noErr) + long readByteCount = byteCount; + theErr = FSRead(houseRefNum, &readByteCount, *thisHouse); + if (theErr != noErr || readByteCount != byteCount || byteCount < static_cast(houseType::kBinaryDataSize)) { CheckFileError(theErr, thisHouseName); HUnlock((Handle)thisHouse); return(false); } + if (alignmentPadding != 0) + { + // GP: Correct for padding + const size_t roomDataSize = byteCount - houseType::kBinaryDataSize; + + uint8_t *houseDataBytes = reinterpret_cast(*thisHouse); + memmove((*thisHouse)->rooms, houseDataBytes + houseType::kBinaryDataSize, roomDataSize); + } + ByteSwapHouse(*thisHouse, static_cast(byteCount)); numberRooms = (*thisHouse)->nRooms; @@ -791,14 +807,27 @@ Boolean WriteHouse (Boolean checkIt) ByteSwapHouse(*thisHouse, static_cast(byteCount)); - theErr = FSWrite(houseRefNum, &byteCount, *thisHouse); + long headerSize = houseType::kBinaryDataSize; + long roomsSize = sizeof(roomType) * (*thisHouse)->nRooms; + + theErr = FSWrite(houseRefNum, &headerSize, *thisHouse); if (theErr != noErr) { CheckFileError(theErr, thisHouseName); + ByteSwapHouse(*thisHouse, static_cast(byteCount)); HUnlock((Handle)thisHouse); return(false); } - + + theErr = FSWrite(houseRefNum, &roomsSize, (*thisHouse)->rooms); + if (theErr != noErr) + { + CheckFileError(theErr, thisHouseName); + ByteSwapHouse(*thisHouse, static_cast(byteCount)); + HUnlock((Handle)thisHouse); + return(false); + } + ByteSwapHouse(*thisHouse, static_cast(byteCount)); theErr = SetEOF(houseRefNum, byteCount); diff --git a/GpApp/Play.cpp b/GpApp/Play.cpp index 5a44558..f1001f2 100644 --- a/GpApp/Play.cpp +++ b/GpApp/Play.cpp @@ -74,7 +74,6 @@ extern Boolean switchedOut; void NewGame (short mode) { Rect tempRect; - Size freeBytes, growBytes; OSErr theErr; Boolean wasPlayMusicPref; @@ -192,8 +191,6 @@ void NewGame (short mode) InitTelephone(); wasPlayMusicPref = isPlayMusicGame; - freeBytes = MaxMem(&growBytes); - #ifdef CREATEDEMODATA SysBeep(1); #endif diff --git a/GpApp/Render.cpp b/GpApp/Render.cpp index 94a3b87..0a8c00e 100644 --- a/GpApp/Render.cpp +++ b/GpApp/Render.cpp @@ -616,14 +616,18 @@ void RenderShreds (void) void CopyRectsQD (void) { short i; + + CGrafPtr mainWindowGraf = GetWindowPort(mainWindow); for (i = 0; i < numWork2Main; i++) { CopyBits((BitMap *)*GetGWorldPixMap(workSrcMap), - GetPortBitMapForCopyBits(GetWindowPort(mainWindow)), + GetPortBitMapForCopyBits(mainWindowGraf), &work2MainRects[i], &work2MainRects[i], srcCopy, nil); } + + mainWindowGraf->m_port.SetDirty(PortabilityLayer::QDPortDirtyFlag_Contents); for (i = 0; i < numBack2Work; i++) { @@ -661,6 +665,7 @@ void RenderFrame (void) while (TickCount() < nextFrame) { + Delay(1, nullptr); } nextFrame = TickCount() + kTicksPerFrame; diff --git a/GpApp/Transitions.cpp b/GpApp/Transitions.cpp index 927a7e4..3f2681d 100644 --- a/GpApp/Transitions.cpp +++ b/GpApp/Transitions.cpp @@ -138,8 +138,12 @@ void WipeScreenOn (short direction, Rect *theRect) void DumpScreenOn (Rect *theRect) { + CGrafPtr graf = GetWindowPort(mainWindow); + CopyBits((BitMap *)*GetGWorldPixMap(workSrcMap), - GetPortBitMapForCopyBits(GetWindowPort(mainWindow)), + GetPortBitMapForCopyBits(graf), theRect, theRect, srcCopy, nil); + + graf->m_port.SetDirty(PortabilityLayer::QDPortDirtyFlag_Contents); } diff --git a/GpApp/Utilities.cpp b/GpApp/Utilities.cpp index 314a948..39f3e5e 100644 --- a/GpApp/Utilities.cpp +++ b/GpApp/Utilities.cpp @@ -248,8 +248,9 @@ OSErr CreateOffScreenGWorld (GWorldPtr *theGWorld, Rect *bounds, short depth) if (theErr) theErr = NewGWorld(theGWorld, depth, bounds, nil, nil, 0); - - LockPixels(GetGWorldPixMap(*theGWorld)); + + if (!theErr) + LockPixels(GetGWorldPixMap(*theGWorld)); return theErr; } @@ -450,6 +451,8 @@ Boolean WaitForInputEvent (short seconds) } if ((seconds != -1) && (TickCount() >= timeToBail)) waiting = false; + + Delay(1, nullptr); } FlushEvents(everyEvent, 0); return (didResume); diff --git a/GpCommon/GpPixelFormat.h b/GpCommon/GpPixelFormat.h index 8d6f362..e48fc03 100644 --- a/GpCommon/GpPixelFormat.h +++ b/GpCommon/GpPixelFormat.h @@ -6,6 +6,7 @@ namespace GpPixelFormats { kInvalid, + kBW1, k8BitStandard, k8BitCustom, kRGB555, diff --git a/PortabilityLayer/MenuManager.cpp b/PortabilityLayer/MenuManager.cpp index 2d2fa5b..91bdc45 100644 --- a/PortabilityLayer/MenuManager.cpp +++ b/PortabilityLayer/MenuManager.cpp @@ -18,39 +18,44 @@ #include "QDManager.h" #include "QDPixMap.h" #include "RGBAColor.h" +#include "Vec2i.h" #include #include +#include namespace { + static const int kMidGray = 187; + const PortabilityLayer::RGBAColor gs_barTopLeftCornerGraphicPixels[] = { { 0, 0, 0, 255 }, { 0, 0, 0, 255 }, { 0, 0, 0, 255 }, { 85, 85, 85, 255 }, { 170, 170, 170, 255 }, { 0, 0, 0, 255 }, { 0, 0, 0, 255 }, { 85, 85, 85, 255 }, { 255, 255, 255, 255 }, { 255, 255, 255, 255 }, - { 0, 0, 0, 255 }, { 85, 85, 85, 255 }, { 255, 255, 255, 255 }, { 221, 221, 221, 255 }, { 221, 221, 221, 255 }, - { 85, 85, 85, 255 }, { 255, 255, 255, 255 }, { 221, 221, 221, 255 }, { 221, 221, 221, 255 }, { 221, 221, 221, 255 }, - { 170, 170, 170, 255 }, { 255, 255, 255, 255 }, { 221, 221, 221, 255 }, { 221, 221, 221, 255 }, { 221, 221, 221, 255 }, + { 0, 0, 0, 255 }, { 85, 85, 85, 255 }, { 255, 255, 255, 255 }, { kMidGray, kMidGray, kMidGray, 255 }, { kMidGray, kMidGray, kMidGray, 255 }, + { 85, 85, 85, 255 }, { 255, 255, 255, 255 }, { kMidGray, kMidGray, kMidGray, 255 }, { kMidGray, kMidGray, kMidGray, 255 }, { kMidGray, kMidGray, kMidGray, 255 }, + { 170, 170, 170, 255 }, { 255, 255, 255, 255 }, { kMidGray, kMidGray, kMidGray, 255 }, { kMidGray, kMidGray, kMidGray, 255 }, { kMidGray, kMidGray, kMidGray, 255 }, }; const PortabilityLayer::RGBAColor gs_barTopRightCornerGraphicPixels[] = { { 170, 170, 170, 255 }, { 85, 85, 85, 255 }, { 0, 0, 0, 255 }, { 0, 0, 0, 255 }, { 0, 0, 0, 255 }, { 255, 255, 255, 255 }, { 255, 255, 255, 255 }, { 85, 85, 85, 255 }, { 0, 0, 0, 255 }, { 0, 0, 0, 255 }, - { 221, 221, 221, 255 }, { 221, 221, 221, 255 }, { 255, 255, 255, 255 }, { 85, 85, 85, 255 }, { 0, 0, 0, 255 }, - { 221, 221, 221, 255 }, { 221, 221, 221, 255 }, { 221, 221, 221, 255 }, { 255, 255, 255, 255 }, { 85, 85, 85, 255 }, - { 221, 221, 221, 255 }, { 221, 221, 221, 255 }, { 221, 221, 221, 255 }, { 255, 255, 255, 255 }, { 85, 85, 85, 255 }, + { kMidGray, kMidGray, kMidGray, 255 }, { kMidGray, kMidGray, kMidGray, 255 }, { 255, 255, 255, 255 }, { 85, 85, 85, 255 }, { 0, 0, 0, 255 }, + { kMidGray, kMidGray, kMidGray, 255 }, { kMidGray, kMidGray, kMidGray, 255 }, { kMidGray, kMidGray, kMidGray, 255 }, { 255, 255, 255, 255 }, { 85, 85, 85, 255 }, + { kMidGray, kMidGray, kMidGray, 255 }, { kMidGray, kMidGray, kMidGray, 255 }, { kMidGray, kMidGray, kMidGray, 255 }, { 255, 255, 255, 255 }, { 85, 85, 85, 255 }, }; const PortabilityLayer::RGBAColor gs_barBrightColor = { 255, 255, 255, 255 }; - const PortabilityLayer::RGBAColor gs_barMidColor = { 221, 221, 221, 255 }; + const PortabilityLayer::RGBAColor gs_barMidColor = { kMidGray, kMidGray, kMidGray, 255 }; const PortabilityLayer::RGBAColor gs_barDarkColor = { 102, 102, 102, 255 }; const PortabilityLayer::RGBAColor gs_barBottomEdgeColor = { 0, 0, 0, 255 }; const PortabilityLayer::RGBAColor gs_barNormalTextColor = { 0, 0, 0, 255 }; + const PortabilityLayer::RGBAColor gs_barHighlightTextColor = { 255, 255, 255, 255 }; - const PortabilityLayer::RGBAColor gs_barHighlightBrightColor = { 153, 153, 255, 255 }; - const PortabilityLayer::RGBAColor gs_barHighlightMidColor = { 102, 102, 204, 255 }; - const PortabilityLayer::RGBAColor gs_barHighlightDarkColor = { 51, 51, 102, 255 }; + const PortabilityLayer::RGBAColor gs_barHighlightBrightColor = { 153, 204, 255, 255 }; + const PortabilityLayer::RGBAColor gs_barHighlightMidColor = { 51, 102, 204, 255 }; + const PortabilityLayer::RGBAColor gs_barHighlightDarkColor = { 0, 51, 102, 255 }; PortabilityLayer::SimpleGraphicInstanceRGBA<5, 5> gs_barTopLeftCornerGraphic(gs_barTopLeftCornerGraphicPixels); PortabilityLayer::SimpleGraphicInstanceRGBA<5, 5> gs_barTopRightCornerGraphic(gs_barTopRightCornerGraphicPixels); @@ -65,6 +70,9 @@ struct MenuItem uint8_t textStyle; bool enabled; bool checked; + + uint16_t layoutYOffset; + uint16_t layoutHeight; }; struct Menu @@ -75,6 +83,10 @@ struct Menu uint16_t commandID; bool enabled; bool isIcon; + bool haveMenuLayout; + + size_t layoutWidth; + size_t layoutHeight; PortabilityLayer::MMHandleBlock *stringBlobHandle; @@ -83,6 +95,7 @@ struct Menu // Refreshed on layout size_t cumulativeOffset; + size_t unpaddedTitleWidth; unsigned int menuIndex; size_t numMenuItems; @@ -112,6 +125,9 @@ namespace PortabilityLayer void SetItemEnabled(Menu **menu, unsigned int index, bool enabled) override; void SetItemChecked(Menu **menu, unsigned int index, bool checked) override; + bool IsPointInMenuBar(const Vec2i &point) const override; + void MenuSelect(const Vec2i &initialPoint, int16_t *outMenu, uint16_t *outItem) override; + void DrawMenuBar() override; void RenderFrame(IGpDisplayDriver *displayDriver) override; @@ -119,7 +135,36 @@ namespace PortabilityLayer static MenuManagerImpl *GetInstance(); private: - void RefreshMenuLayout(); + class MenuSelectionState + { + public: + MenuSelectionState(); + ~MenuSelectionState(); + + void HandleSelectionOfMenu(MenuManagerImpl *mm, Menu **menuHdl, bool &outNeedRedraw); + void Dismiss(); + + Menu **GetSelectedMenu() const; + CGraf *GetRenderedMenu() const; + const unsigned int *GetSelectedItem() const; + + void SelectItem(size_t item); + void ClearSelection(); + + private: + void RenderMenu(Menu *menu); + + Menu **m_currentMenu; + CGraf *m_menuGraf; + unsigned int m_itemIndex; + bool m_haveItem; + }; + + void RefreshMenuBarLayout(); + void RefreshMenuLayout(Menu *menu); + void ProcessMouseMoveTo(const Vec2i &point); + void ProcessMouseMoveToMenuBar(const Vec2i &point); + void ProcessMouseMoveToMenu(const Vec2i &point); static const unsigned int kIconResID = 128; static const unsigned int kMenuFontSize = 12; @@ -128,13 +173,21 @@ namespace PortabilityLayer static const unsigned int kMenuBarHeight = 20; static const unsigned int kMenuBarItemPadding = 6; static const unsigned int kMenuBarInitialPadding = 16; + + static const unsigned int kMenuItemHeight = 18; + static const unsigned int kMenuItemTextYOffset = 13; + static const unsigned int kMenuSeparatorHeight = 6; + + static const unsigned int kMenuItemRightPadding = 8; + static const unsigned int kMenuItemLeftPadding = 16 + 2 + 2; // 2 for left border, 16 for icon, 2 for spacing + static const int kMenuFontFlags = PortabilityLayer::FontFamilyFlag_Bold; CGraf *m_menuBarGraf; Menu **m_firstMenu; Menu **m_lastMenu; - bool m_haveMenuLayout; + bool m_haveMenuBarLayout; bool m_haveIcon; uint8_t m_iconColors[16 * 16]; @@ -142,6 +195,8 @@ namespace PortabilityLayer SimpleGraphic *m_iconGraphic; + MenuSelectionState m_menuSelectionState; + static MenuManagerImpl ms_instance; }; @@ -149,7 +204,7 @@ namespace PortabilityLayer : m_menuBarGraf(nullptr) , m_firstMenu(nullptr) , m_lastMenu(nullptr) - , m_haveMenuLayout(false) + , m_haveMenuBarLayout(false) , m_haveIcon(false) , m_iconGraphic(nullptr) { @@ -235,7 +290,9 @@ namespace PortabilityLayer menu->enabled = ((enableFlags & 1) != 0); menu->menuIndex = 0; menu->cumulativeOffset = 0; + menu->unpaddedTitleWidth = 0; menu->isIcon = false; + menu->haveMenuLayout = false; uint8_t *stringDataStart = static_cast(stringData->m_contents); uint8_t *stringDest = stringDataStart; @@ -258,6 +315,7 @@ namespace PortabilityLayer currentItem->nameOffsetInStringBlob = static_cast(stringDest - stringDataStart); currentItem->enabled = ((enableFlags & 1) != 0); currentItem->checked = false; + currentItem->layoutYOffset = 0; enableFlags >>= 1; @@ -269,6 +327,8 @@ namespace PortabilityLayer menu->stringBlobHandle = stringData; menu->prevMenu = nullptr; menu->nextMenu = nullptr; + menu->layoutWidth = 0; + menu->layoutHeight = 0; return reinterpret_cast(&menuData->m_contents); } @@ -286,7 +346,7 @@ namespace PortabilityLayer void MenuManagerImpl::InsertMenuBefore(Menu **insertingMenu, Menu **existingMenu) { - m_haveMenuLayout = false; + m_haveMenuBarLayout = false; Menu *insertingMenuPtr = *insertingMenu; Menu *existingMenuPtr = *existingMenu; @@ -304,7 +364,7 @@ namespace PortabilityLayer void MenuManagerImpl::InsertMenuAfter(Menu **insertingMenu, Menu **existingMenu) { - m_haveMenuLayout = false; + m_haveMenuBarLayout = false; Menu *insertingMenuPtr = *insertingMenu; Menu *existingMenuPtr = *existingMenu; @@ -322,7 +382,7 @@ namespace PortabilityLayer void MenuManagerImpl::InsertMenuAtEnd(Menu **insertingMenu) { - m_haveMenuLayout = false; + m_haveMenuBarLayout = false; if (m_firstMenu == nullptr) { @@ -337,7 +397,7 @@ namespace PortabilityLayer void MenuManagerImpl::InsertMenuAtBeginning(Menu **insertingMenu) { - m_haveMenuLayout = false; + m_haveMenuBarLayout = false; if (m_firstMenu == nullptr) { @@ -377,6 +437,72 @@ namespace PortabilityLayer menu->menuItems[index].checked = checked; } + bool MenuManagerImpl::IsPointInMenuBar(const Vec2i &point) const + { + return point.m_y >= 0 && static_cast(point.m_y) < kMenuBarHeight; + } + + + void MenuManagerImpl::MenuSelect(const Vec2i &initialPoint, int16_t *outMenu, uint16_t *outItem) + { + RefreshMenuBarLayout(); + + ProcessMouseMoveTo(initialPoint); + + if (m_menuSelectionState.GetSelectedMenu() == nullptr) + { + if (outMenu) + *outMenu = 0; + + if (outItem) + *outItem = 0; + + return; + } + + EventRecord evt; + bool canDismiss = false; + while (!canDismiss) + { + if (WaitNextEvent(everyEvent, &evt, 1, nullptr)) + { + const EventCode eventCode = static_cast(evt.what); + + switch (eventCode) + { + case mouseMove: + case mouseDown: // It's possible to get a mouse down event again if the mouse leaves the window and is downed again inside + ProcessMouseMoveTo(PortabilityLayer::Vec2i(evt.where.h, evt.where.v)); + break; + case mouseUp: + canDismiss = true; + break; + } + } + } + + if (outMenu) + *outMenu = 0; + + if (outItem) + *outItem = 0; + + if (Menu **menuHdl = m_menuSelectionState.GetSelectedMenu()) + { + if (const unsigned int *selectedItem = m_menuSelectionState.GetSelectedItem()) + { + if (outMenu) + *outMenu = (*menuHdl)->menuID; + + if (outItem) + *outItem = (*selectedItem) + 1; + } + } + + m_menuSelectionState.Dismiss(); + this->DrawMenuBar(); + } + void MenuManagerImpl::DrawMenuBar() { if (!m_haveIcon) @@ -422,17 +548,7 @@ namespace PortabilityLayer if (m_menuBarGraf == nullptr) { - int depth = 0; - - switch (pixelFormat) - { - case GpPixelFormats::k8BitStandard: - depth = 8; - break; - default: - PL_NotYetImplemented(); - return; - } + int depth = PortabilityLayer::QDManager::GetInstance()->DepthForPixelFormat(pixelFormat); if (qdManager->NewGWorld(&m_menuBarGraf, depth, menuRect, nullptr, nullptr, 0) != 0) return; @@ -448,8 +564,7 @@ namespace PortabilityLayer return; } - - RefreshMenuLayout(); + RefreshMenuBarLayout(); CGraf *oldGraf; GDHandle oldDevice; @@ -504,11 +619,44 @@ namespace PortabilityLayer gs_barTopLeftCornerGraphic.DrawToPixMap(pixMap, 0, 0); gs_barTopRightCornerGraphic.DrawToPixMap(pixMap, static_cast(width) - static_cast(gs_barTopRightCornerGraphic.m_width), 0); + Menu **selectedMenuHdl = m_menuSelectionState.GetSelectedMenu(); + + if (selectedMenuHdl) + { + Menu *menu = *selectedMenuHdl; + + const size_t xCoordinate = menu->cumulativeOffset + (menu->menuIndex * 2) * kMenuBarItemPadding + kMenuBarInitialPadding - kMenuBarItemPadding; + const size_t width = menu->unpaddedTitleWidth + kMenuBarItemPadding * 2; + + const int16_t left = static_cast(xCoordinate); + const int16_t right = static_cast(xCoordinate + width); + + // Top edge + qdState->SetForeColor(gs_barHighlightBrightColor); + { + const Rect rect = Rect::Create(0, left, 1, right); + PaintRect(&rect); + } + + // Middle + qdState->SetForeColor(gs_barHighlightMidColor); + { + const Rect rect = Rect::Create(1, left, kMenuBarHeight - 2, right); + PaintRect(&rect); + } + + qdState->SetForeColor(gs_barHighlightDarkColor); + { + const Rect rect = Rect::Create(kMenuBarHeight - 2, left, kMenuBarHeight - 1, right); + PaintRect(&rect); + } + } + + // Text items qdState->SetForeColor(gs_barNormalTextColor); TextFont(systemFont); TextSize(kMenuFontSize); - // Text items { Menu **menuHdl = m_firstMenu; while (menuHdl) @@ -522,13 +670,16 @@ namespace PortabilityLayer if (menu->isIcon) { if (m_iconGraphic) - m_iconGraphic->DrawToPixMapWithMask(pixMap, m_iconMask, xCoordinate, kMenuBarIconYOffset); + m_iconGraphic->DrawToPixMapWithMask(pixMap, m_iconMask, static_cast(xCoordinate), kMenuBarIconYOffset); } else { - qdState->m_penPos.h = xCoordinate; - qdState->m_penPos.v = kMenuBarTextYOffset; - DrawString(PLPasStr(static_cast(menu->stringBlobHandle->m_contents))); + if (menuHdl != selectedMenuHdl) + { + qdState->m_penPos.h = static_cast(xCoordinate); + qdState->m_penPos.v = kMenuBarTextYOffset; + DrawString(PLPasStr(static_cast(menu->stringBlobHandle->m_contents))); + } } } @@ -536,6 +687,21 @@ namespace PortabilityLayer } } + if (selectedMenuHdl) + { + Menu *menu = *selectedMenuHdl; + + if (menu->stringBlobHandle && !menu->isIcon) + { + qdState->SetForeColor(gs_barHighlightTextColor); + size_t xCoordinate = menu->cumulativeOffset + (menu->menuIndex * 2) * kMenuBarItemPadding + kMenuBarInitialPadding; + + qdState->m_penPos.h = static_cast(xCoordinate); + qdState->m_penPos.v = kMenuBarTextYOffset; + DrawString(PLPasStr(static_cast(menu->stringBlobHandle->m_contents))); + } + } + SetGWorld(oldGraf, oldDevice); m_menuBarGraf->m_port.SetDirty(QDPortDirtyFlag_Contents); @@ -555,11 +721,23 @@ namespace PortabilityLayer displayDriver->DrawSurface(m_menuBarGraf->m_ddSurface, 0, 0, width, height); } } + + if (CGraf *renderedMenu = m_menuSelectionState.GetRenderedMenu()) + { + renderedMenu->PushToDDSurface(displayDriver); + + Menu *selectedMenu = *m_menuSelectionState.GetSelectedMenu(); + const PixMap *pixMap = *renderedMenu->m_port.GetPixMap(); + + const size_t xCoordinate = kMenuBarInitialPadding + selectedMenu->menuIndex * kMenuBarItemPadding * 2 + selectedMenu->cumulativeOffset - kMenuBarItemPadding; + + displayDriver->DrawSurface(renderedMenu->m_ddSurface, xCoordinate, kMenuBarHeight, pixMap->m_rect.right, pixMap->m_rect.bottom); + } } - void MenuManagerImpl::RefreshMenuLayout() + void MenuManagerImpl::RefreshMenuBarLayout() { - if (m_haveMenuLayout) + if (m_haveMenuBarLayout) return; PortabilityLayer::FontManager *fontManager = PortabilityLayer::FontManager::GetInstance(); @@ -587,16 +765,141 @@ namespace PortabilityLayer if (pascalStr.Length() == 1 && pascalStr.UChars()[0] == 0x14) { - measuredWidth += 16; + menu->unpaddedTitleWidth = 16; menu->isIcon = true; } else - measuredWidth += rfont->MeasureString(pascalStr.UChars(), pascalStr.Length()); + menu->unpaddedTitleWidth = rfont->MeasureString(pascalStr.UChars(), pascalStr.Length()); + + measuredWidth += menu->unpaddedTitleWidth; menuHdl = menu->nextMenu; } - m_haveMenuLayout = true; + m_haveMenuBarLayout = true; + } + + void MenuManagerImpl::RefreshMenuLayout(Menu *menu) + { + if (menu->haveMenuLayout) + return; + + PortabilityLayer::FontManager *fontManager = PortabilityLayer::FontManager::GetInstance(); + + PortabilityLayer::FontFamily *fontFamily = PortabilityLayer::FontManager::GetInstance()->GetSystemFont(kMenuFontSize, kMenuFontFlags); + if (!fontFamily) + return; + + PortabilityLayer::RenderedFont *rfont = PortabilityLayer::FontManager::GetInstance()->GetRenderedFontFromFamily(fontFamily, kMenuFontSize, kMenuFontFlags); + if (!rfont) + return; + + const uint8_t *strBlob = static_cast(menu->stringBlobHandle->m_contents); + + size_t cumulativeHeight = 0; + size_t width = menu->width; + + const size_t numItems = menu->numMenuItems; + for (size_t i = 0; i < numItems; i++) + { + MenuItem &item = menu->menuItems[i]; + item.layoutYOffset = cumulativeHeight; + item.layoutHeight = kMenuItemHeight; + + const uint8_t *itemName = strBlob + item.nameOffsetInStringBlob; + const PLPasStr itemNamePStr = PLPasStr(itemName); + + const size_t nameWidth = rfont->MeasureString(itemNamePStr.UChars(), itemNamePStr.Length()); + + const size_t paddedWidth = nameWidth + kMenuItemLeftPadding + kMenuItemRightPadding; + + width = std::max(width, paddedWidth); + + cumulativeHeight += item.layoutHeight; + } + + menu->haveMenuLayout = true; + menu->layoutWidth = width; + menu->layoutHeight = cumulativeHeight; + } + + void MenuManagerImpl::ProcessMouseMoveTo(const Vec2i &point) + { + if (point.m_y < 0) + return; + + if (point.m_y < static_cast(kMenuBarHeight)) + ProcessMouseMoveToMenuBar(point); + else + ProcessMouseMoveToMenu(point); + } + + void MenuManagerImpl::ProcessMouseMoveToMenuBar(const Vec2i &point) + { + if (point.m_x < 0) + return; + + const uint32_t mouseXCoordinate = static_cast(point.m_x); + + Menu **selectedMenu = nullptr; + + Menu **menuHdl = m_firstMenu; + + while (menuHdl) + { + Menu *menu = *menuHdl; + + uint32_t menuLeftXCoordinate = kMenuBarInitialPadding + menu->cumulativeOffset + menu->menuIndex * kMenuBarItemPadding * 2 - kMenuBarItemPadding; + uint32_t menuRightXCoordinate = menuLeftXCoordinate + menu->unpaddedTitleWidth + kMenuBarItemPadding * 2; + + if (mouseXCoordinate >= menuLeftXCoordinate && mouseXCoordinate < menuRightXCoordinate) + { + selectedMenu = menuHdl; + break; + } + + menuHdl = menu->nextMenu; + } + + if (selectedMenu) + { + bool needRedraw = false; + m_menuSelectionState.HandleSelectionOfMenu(this, menuHdl, needRedraw); + + if (needRedraw) + DrawMenuBar(); + } + } + + void MenuManagerImpl::ProcessMouseMoveToMenu(const Vec2i &point) + { + Menu **selectedMenuHandle = m_menuSelectionState.GetSelectedMenu(); + + if (selectedMenuHandle) + { + Menu *menu = *selectedMenuHandle; + const int32_t xCoordinate = kMenuBarInitialPadding + menu->menuIndex * kMenuBarItemPadding * 2 + menu->cumulativeOffset - kMenuBarItemPadding; + + const Vec2i localPoint = point - Vec2i(xCoordinate, kMenuBarHeight); + + if (localPoint.m_x < 0 || localPoint.m_y < 0 || static_cast(localPoint.m_x) >= menu->layoutWidth || static_cast(localPoint.m_y) >= menu->layoutHeight) + return; + + const size_t localY = localPoint.m_y; + + for (size_t i = 0; i < menu->numMenuItems; i++) + { + const MenuItem &item = menu->menuItems[i]; + + if (localY >= item.layoutYOffset && localY - item.layoutYOffset < item.layoutHeight) + { + m_menuSelectionState.SelectItem(i); + return; + } + } + } + + m_menuSelectionState.ClearSelection(); } MenuManagerImpl *MenuManagerImpl::GetInstance() @@ -606,6 +909,166 @@ namespace PortabilityLayer MenuManagerImpl MenuManagerImpl::ms_instance; + MenuManagerImpl::MenuSelectionState::MenuSelectionState() + : m_currentMenu(nullptr) + , m_menuGraf(nullptr) + , m_haveItem(false) + , m_itemIndex(0) + { + } + + MenuManagerImpl::MenuSelectionState::~MenuSelectionState() + { + Dismiss(); + } + + void MenuManagerImpl::MenuSelectionState::HandleSelectionOfMenu(MenuManagerImpl *mm, Menu **menuHdl, bool &outNeedRedraw) + { + outNeedRedraw = false; + + if (!menuHdl) + return; + + if (menuHdl != m_currentMenu) + { + Dismiss(); + m_currentMenu = menuHdl; + + Menu *menu = *menuHdl; + + outNeedRedraw = true; + + mm->RefreshMenuLayout(menu); + + RenderMenu(menu); + } + } + + void MenuManagerImpl::MenuSelectionState::Dismiss() + { + if (m_menuGraf) + { + DisposeGWorld(m_menuGraf); + m_menuGraf = nullptr; + } + + m_currentMenu = nullptr; + m_haveItem = false; + } + + Menu **MenuManagerImpl::MenuSelectionState::GetSelectedMenu() const + { + return m_currentMenu; + } + + CGraf *MenuManagerImpl::MenuSelectionState::GetRenderedMenu() const + { + return m_menuGraf; + } + + const unsigned int *MenuManagerImpl::MenuSelectionState::GetSelectedItem() const + { + if (!m_haveItem) + return nullptr; + + return &m_itemIndex; + } + + void MenuManagerImpl::MenuSelectionState::SelectItem(size_t item) + { + if (m_haveItem && m_itemIndex == item) + return; + + m_haveItem = true; + m_itemIndex = item; + + if (m_currentMenu) + RenderMenu(*m_currentMenu); + } + + void MenuManagerImpl::MenuSelectionState::ClearSelection() + { + if (!m_haveItem) + return; + + m_haveItem = false; + + if (m_currentMenu) + RenderMenu(*m_currentMenu); + } + + void MenuManagerImpl::MenuSelectionState::RenderMenu(Menu *menu) + { + PortabilityLayer::QDManager *qdManager = PortabilityLayer::QDManager::GetInstance(); + + const Rect menuRect = Rect::Create(0, 0, menu->layoutHeight, menu->layoutWidth); + + if (m_menuGraf == nullptr) + { + GpPixelFormat_t pixelFormat; + PortabilityLayer::HostDisplayDriver::GetInstance()->GetDisplayResolution(nullptr, nullptr, &pixelFormat); + + if (qdManager->NewGWorld(&m_menuGraf, qdManager->DepthForPixelFormat(pixelFormat), menuRect, nullptr, nullptr, 0) != 0) + return; + } + + CGrafPtr oldGraf = nullptr; + GDHandle oldDevice = nullptr; + GetGWorld(&oldGraf, &oldDevice); + + SetGWorld(m_menuGraf, nullptr); + + QDState *qdState = qdManager->GetState(); + + qdState->SetForeColor(gs_barMidColor); + + { + const Rect rect = Rect::Create(0, 0, menu->layoutHeight, menu->layoutWidth); + PaintRect(&rect); + } + + TextFont(systemFont); + TextSize(kMenuFontSize); + + const uint8_t *strBlob = static_cast(menu->stringBlobHandle->m_contents); + + qdState->m_penPos.h = kMenuItemLeftPadding; + + qdState->SetForeColor(gs_barNormalTextColor); + + for (size_t i = 0; i < menu->numMenuItems; i++) + { + if (m_haveItem && i == m_itemIndex) + continue; + + const MenuItem &item = menu->menuItems[i]; + + qdState->m_penPos.v = item.layoutYOffset + kMenuItemTextYOffset; + + DrawString(PLPasStr(strBlob + item.nameOffsetInStringBlob)); + } + + if (m_haveItem) + { + const MenuItem &selectedItem = menu->menuItems[m_itemIndex]; + qdState->SetForeColor(gs_barHighlightMidColor); + const Rect itemRect = Rect::Create(selectedItem.layoutYOffset, 0, selectedItem.layoutYOffset + selectedItem.layoutHeight, menu->layoutWidth); + PaintRect(&itemRect); + + qdState->SetForeColor(gs_barHighlightTextColor); + + const MenuItem &item = menu->menuItems[m_itemIndex]; + + qdState->m_penPos.v = item.layoutYOffset + kMenuItemTextYOffset; + + DrawString(PLPasStr(strBlob + item.nameOffsetInStringBlob)); + } + + m_menuGraf->m_port.SetDirty(QDPortDirtyFlag_Contents); + + SetGWorld(oldGraf, oldDevice); + } + MenuManager *MenuManager::GetInstance() { return MenuManagerImpl::GetInstance(); diff --git a/PortabilityLayer/MenuManager.h b/PortabilityLayer/MenuManager.h index 8627554..310045b 100644 --- a/PortabilityLayer/MenuManager.h +++ b/PortabilityLayer/MenuManager.h @@ -1,10 +1,14 @@ #pragma once +#include + +struct IGpDisplayDriver; struct Menu; -struct IGpDisplayDriver; namespace PortabilityLayer { + struct Vec2i; + class MenuManager { public: @@ -21,6 +25,10 @@ namespace PortabilityLayer virtual void SetItemEnabled(Menu **menu, unsigned int index, bool enabled) = 0; virtual void SetItemChecked(Menu **menu, unsigned int index, bool checked) = 0; + virtual bool IsPointInMenuBar(const Vec2i &point) const = 0; + + virtual void MenuSelect(const Vec2i &initialPoint, int16_t *outMenu, uint16_t *outItem) = 0; + virtual void DrawMenuBar() = 0; virtual void RenderFrame(IGpDisplayDriver *displayDriver) = 0; diff --git a/PortabilityLayer/PLCore.cpp b/PortabilityLayer/PLCore.cpp index 0f63ec2..125b8fc 100644 --- a/PortabilityLayer/PLCore.cpp +++ b/PortabilityLayer/PLCore.cpp @@ -7,6 +7,7 @@ #include "FileManager.h" #include "FilePermission.h" #include "FontManager.h" +#include "GpVOSEvent.h" #include "HostDirectoryCursor.h" #include "HostFileSystem.h" #include "HostSuspendCallArgument.h" @@ -27,10 +28,12 @@ #include "PLBigEndian.h" #include "PLEventQueue.h" #include "QDManager.h" +#include "Vec2i.h" #include "WindowDef.h" #include "WindowManager.h" #include +#include static bool ConvertFilenameToSafePStr(const char *str, uint8_t *pstr) { @@ -55,16 +58,65 @@ static bool ConvertFilenameToSafePStr(const char *str, uint8_t *pstr) return true; } -static void TranslateVOSEvent(const GpVOSEvent *vosEvent, EventRecord *evt) +static void TranslateMouseInputEvent(const GpMouseInputEvent &vosEvent, PortabilityLayer::EventQueue *queue) { - PL_NotYetImplemented(); + if (vosEvent.m_button == GpMouseButtons::kLeft) + { + if (vosEvent.m_eventType == GpMouseEventTypes::kDown) + { + if (EventRecord *evt = queue->Enqueue()) + { + evt->what = mouseDown; + evt->where.h = std::min(INT16_MAX, std::max(INT16_MIN, vosEvent.m_x)); + evt->where.v = std::min(INT16_MAX, std::max(INT16_MIN, vosEvent.m_y)); + } + } + else if (vosEvent.m_eventType == GpMouseEventTypes::kUp) + { + if (EventRecord *evt = queue->Enqueue()) + { + evt->what = mouseUp; + evt->where.h = std::min(INT16_MAX, std::max(INT16_MIN, vosEvent.m_x)); + evt->where.v = std::min(INT16_MAX, std::max(INT16_MIN, vosEvent.m_y)); + } + } + } + else if (vosEvent.m_eventType == GpMouseEventTypes::kMove) + { + if (EventRecord *evt = queue->Enqueue()) + { + evt->what = mouseMove; + evt->where.h = std::min(INT16_MAX, std::max(INT16_MIN, vosEvent.m_x)); + evt->where.v = std::min(INT16_MAX, std::max(INT16_MIN, vosEvent.m_y)); + } + } +} + +static void TranslateKeyboardInputEvent(const GpKeyboardInputEvent &vosEvent, PortabilityLayer::EventQueue *queue) +{ +} + +static void TranslateVOSEvent(const GpVOSEvent *vosEvent, PortabilityLayer::EventQueue *queue) +{ + switch (vosEvent->m_eventType) + { + case GpVOSEventTypes::kMouseInput: + TranslateMouseInputEvent(vosEvent->m_event.m_mouseInputEvent, queue); + break; + case GpVOSEventTypes::kKeyboardInput: + TranslateKeyboardInputEvent(vosEvent->m_event.m_keyboardInputEvent, queue); + break; + } } static void ImportVOSEvents() { + PortabilityLayer::EventQueue *plQueue = PortabilityLayer::EventQueue::GetInstance(); + PortabilityLayer::HostVOSEventQueue *evtQueue = PortabilityLayer::HostVOSEventQueue::GetInstance(); while (const GpVOSEvent *evt = evtQueue->GetNext()) { + TranslateVOSEvent(evt, plQueue); evtQueue->DischargeOne(); } } @@ -184,8 +236,10 @@ Handle GetResource(const char(&resTypeLiteral)[5], int id) short FindWindow(Point point, WindowPtr *window) { - PL_NotYetImplemented(); - return 0; + short part = 0; + PortabilityLayer::WindowManager::GetInstance()->FindWindow(point, window, &part); + + return part; } void DragWindow(WindowPtr window, Point start, Rect *bounds) @@ -309,6 +363,22 @@ void SetWTitle(WindowPtr window, const PLPasStr &title) PL_NotYetImplemented(); } +bool PeekNextEvent(int32_t eventMask, EventRecord *event) +{ + assert(eventMask == everyEvent); // We don't support other use cases + + PortabilityLayer::EventQueue *queue = PortabilityLayer::EventQueue::GetInstance(); + const EventRecord *record = queue->Peek(); + + if (record) + { + *event = *record; + return PL_TRUE; + } + else + return PL_FALSE; +} + bool GetNextEvent(int32_t eventMask, EventRecord *event) { assert(eventMask == everyEvent); // We don't support other use cases @@ -319,8 +389,12 @@ bool GetNextEvent(int32_t eventMask, EventRecord *event) long MenuSelect(Point point) { - PL_NotYetImplemented(); - return noErr; + int16_t menuID = 0; + uint16_t menuItem = 0; + + PortabilityLayer::MenuManager::GetInstance()->MenuSelect(PortabilityLayer::Vec2i(point.h, point.v), &menuID, &menuItem); + + return (static_cast(menuID) << 16) | (static_cast(menuItem)); } long MenuKey(int charCode) @@ -357,7 +431,8 @@ bool BitTst(const KeyMap *keyMap, int bit) void NumToString(long number, unsigned char *str) { - PL_NotYetImplemented(); + PL_NotYetImplemented_TODO("Strings"); + str[0] = 0; } void ParamText(const PLPasStr &title, const PLPasStr &a, const PLPasStr &b, const PLPasStr &c) @@ -701,7 +776,11 @@ UInt32 GetDblTime() void FlushEvents(int mask, int unknown) { - PL_NotYetImplemented(); + PortabilityLayer::EventQueue *queue = PortabilityLayer::EventQueue::GetInstance(); + + while (queue->Dequeue(nullptr)) + { + } } void ExitToShell() @@ -783,12 +862,6 @@ void DisposePtr(void *ptr) PL_NotYetImplemented(); } -Size MaxMem(Size *growBytes) -{ - PL_NotYetImplemented(); - return 0; -} - void PurgeSpace(long *totalFree, long *contiguousFree) { PL_NotYetImplemented(); diff --git a/PortabilityLayer/PLCore.h b/PortabilityLayer/PLCore.h index 8f028a4..e86c566 100644 --- a/PortabilityLayer/PLCore.h +++ b/PortabilityLayer/PLCore.h @@ -186,8 +186,7 @@ typedef unsigned char KeyMap[16]; enum RegionID { - inDesk, - inMenuBar, + inMenuBar = 1, inSysWindow, inContent, inDrag, @@ -206,6 +205,7 @@ enum EventCode { mouseDown, mouseUp, + mouseMove, keyDown, autoKey, updateEvt, @@ -307,6 +307,7 @@ void MoveWindow(WindowPtr window, int x, int y, Boolean moveToFront); void ShowWindow(WindowPtr window); void SetWTitle(WindowPtr window, const PLPasStr &title); +bool PeekNextEvent(int32_t eventMask, EventRecord *event); bool GetNextEvent(int32_t eventMask, EventRecord *event); long MenuSelect(Point point); // Breaks into menu select routine (in practice we'll just forward one from the queue?) diff --git a/PortabilityLayer/PLEventQueue.cpp b/PortabilityLayer/PLEventQueue.cpp index 3eaac96..a21b68b 100644 --- a/PortabilityLayer/PLEventQueue.cpp +++ b/PortabilityLayer/PLEventQueue.cpp @@ -11,6 +11,7 @@ namespace PortabilityLayer ~EventQueueImpl(); bool Dequeue(EventRecord *evt) override; + const EventRecord *Peek() const override; EventRecord *Enqueue() override; static EventQueueImpl *GetInstance(); @@ -40,7 +41,8 @@ namespace PortabilityLayer if (m_numQueuedEvents == 0) return false; - *evt = m_events[m_firstEvent]; + if (evt) + *evt = m_events[m_firstEvent]; m_firstEvent++; if (m_firstEvent == kMaxEvents) @@ -51,6 +53,15 @@ namespace PortabilityLayer return true; } + const EventRecord *EventQueueImpl::Peek() const + { + if (m_numQueuedEvents == 0) + return nullptr; + + return m_events + m_firstEvent; + } + + EventRecord *EventQueueImpl::Enqueue() { if (m_numQueuedEvents == kMaxEvents) diff --git a/PortabilityLayer/PLEventQueue.h b/PortabilityLayer/PLEventQueue.h index 554258c..eb22826 100644 --- a/PortabilityLayer/PLEventQueue.h +++ b/PortabilityLayer/PLEventQueue.h @@ -2,14 +2,15 @@ #include "PLCore.h" -#include +#include namespace PortabilityLayer -{ +{ class EventQueue { public: virtual bool Dequeue(EventRecord *evt) = 0; + virtual const EventRecord *Peek() const = 0; virtual EventRecord *Enqueue() = 0; static EventQueue *GetInstance(); diff --git a/PortabilityLayer/PLMenus.cpp b/PortabilityLayer/PLMenus.cpp index fcbe4a7..b60eba3 100644 --- a/PortabilityLayer/PLMenus.cpp +++ b/PortabilityLayer/PLMenus.cpp @@ -60,7 +60,7 @@ void DrawMenuBar() void HiliteMenu(int menu) { - PL_NotYetImplemented(); + // Don't know what this does } void EnableMenuItem(MenuHandle menu, int index) diff --git a/PortabilityLayer/PLQDOffscreen.cpp b/PortabilityLayer/PLQDOffscreen.cpp index 794c07c..70640a5 100644 --- a/PortabilityLayer/PLQDOffscreen.cpp +++ b/PortabilityLayer/PLQDOffscreen.cpp @@ -232,7 +232,7 @@ namespace PortabilityLayer } } -OSErr NewGWorld(GWorldPtr *gworld, int depth, Rect *bounds, CTabHandle colorTable, GDHandle device, int flags) +OSErr NewGWorld(GWorldPtr *gworld, int depth, const Rect *bounds, CTabHandle colorTable, GDHandle device, int flags) { return PortabilityLayer::QDManager::GetInstance()->NewGWorld(gworld, depth, *bounds, colorTable, device, flags); } @@ -301,6 +301,7 @@ void DrawPicture(PicHandle pict, Rect *bounds) switch (pixMap->GetPixelFormat()) { + case GpPixelFormats::kBW1: case GpPixelFormats::k8BitStandard: { PortabilityLayer::PixMapBlitEmitter blitEmitter(PortabilityLayer::Vec2i(bounds->left, bounds->top), pixMap); diff --git a/PortabilityLayer/PLQDOffscreen.h b/PortabilityLayer/PLQDOffscreen.h index 106b049..7b3839c 100644 --- a/PortabilityLayer/PLQDOffscreen.h +++ b/PortabilityLayer/PLQDOffscreen.h @@ -25,7 +25,7 @@ enum QDFlags useTempMem = 1, }; -OSErr NewGWorld(GWorldPtr *gworld, int depth, Rect *bounds, CTabHandle colorTable, GDHandle device, int flags); +OSErr NewGWorld(GWorldPtr *gworld, int depth, const Rect *bounds, CTabHandle colorTable, GDHandle device, int flags); void DisposeGWorld(GWorldPtr gworld); PixMapHandle GetGWorldPixMap(GWorldPtr gworld); diff --git a/PortabilityLayer/PLQuickdraw.cpp b/PortabilityLayer/PLQuickdraw.cpp index 28c23bc..27047e3 100644 --- a/PortabilityLayer/PLQuickdraw.cpp +++ b/PortabilityLayer/PLQuickdraw.cpp @@ -534,9 +534,7 @@ void GetIndPattern(Pattern *pattern, int patListID, int index) memcpy(pattern, patternRes + 2 + (index - 1) * 8, 8); } - - -void CopyBits(const BitMap *srcBitmap, BitMap *destBitmap, const Rect *srcRect, const Rect *destRect, CopyBitsMode copyMode, RgnHandle maskRegion) +static void CopyBitsComplete(const BitMap *srcBitmap, const BitMap *maskBitmap, BitMap *destBitmap, const Rect *srcRectBase, const Rect *maskRectBase, const Rect *destRectBase, RgnHandle maskRegion) { assert(srcBitmap->m_pixelFormat == destBitmap->m_pixelFormat); @@ -546,30 +544,84 @@ void CopyBits(const BitMap *srcBitmap, BitMap *destBitmap, const Rect *srcRect, const size_t srcPitch = srcBitmap->m_pitch; const size_t destPitch = destBitmap->m_pitch; - assert(srcRect->top >= srcBounds.top); - assert(srcRect->bottom <= srcBounds.bottom); - assert(srcRect->left >= srcBounds.left); - assert(srcRect->right <= srcBounds.right); + assert(srcRectBase->right - srcRectBase->left == destRectBase->right - destRectBase->left); + assert(srcRectBase->bottom - srcRectBase->top == destRectBase->bottom - destRectBase->top); - assert(destRect->top >= destBounds.top); - assert(destRect->bottom <= destBounds.bottom); - assert(destRect->left >= destBounds.left); - assert(destRect->right <= destBounds.right); + if (maskBitmap) + { + assert(maskRectBase); + assert(maskRectBase->right - maskRectBase->left == destRectBase->right - destRectBase->left); + } - assert(srcRect->right - srcRect->left == destRect->right - destRect->left); - assert(srcRect->bottom - srcRect->top == destRect->bottom - destRect->top); + assert((maskBitmap == nullptr) == (maskRectBase == nullptr)); - const Region *mask = *maskRegion; + Rect srcRect; + Rect destRect; + Rect maskRect; - const Rect constrainedDestRect = destRect->Intersect(mask->rect); + { + const Rect constrainedSrcRect = srcRectBase->Intersect(srcBounds); + const Rect constrainedDestRect = destRectBase->Intersect(destBounds); + + const int32_t leftNudge = std::max(constrainedSrcRect.left - srcRectBase->left, constrainedDestRect.left - destRectBase->left); + const int32_t topNudge = std::max(constrainedSrcRect.top - srcRectBase->top, constrainedDestRect.top - destRectBase->top); + const int32_t bottomNudge = std::min(constrainedSrcRect.bottom - srcRectBase->bottom, constrainedDestRect.bottom - destRectBase->bottom); + const int32_t rightNudge = std::min(constrainedSrcRect.right - srcRectBase->right, constrainedDestRect.right - destRectBase->right); + + const int32_t srcLeft = srcRectBase->left + leftNudge; + const int32_t srcRight = srcRectBase->right + rightNudge; + const int32_t srcTop = srcRectBase->top + topNudge; + const int32_t srcBottom = srcRectBase->bottom + bottomNudge; + + if (srcTop >= srcBottom) + return; + + if (srcLeft >= srcRight) + return; + + srcRect.left = srcLeft; + srcRect.right = srcRight; + srcRect.top = srcTop; + srcRect.bottom = srcBottom; + + destRect.left = destRectBase->left + leftNudge; + destRect.right = destRectBase->right + rightNudge; + destRect.top = destRectBase->top + topNudge; + destRect.bottom = destRectBase->bottom + bottomNudge; + + if (maskRectBase) + { + maskRect.left = maskRectBase->left + leftNudge; + maskRect.right = maskRectBase->right + rightNudge; + maskRect.top = maskRectBase->top + topNudge; + maskRect.bottom = maskRectBase->bottom + bottomNudge; + } + } + + assert(srcRect.top >= srcBounds.top); + assert(srcRect.bottom <= srcBounds.bottom); + assert(srcRect.left >= srcBounds.left); + assert(srcRect.right <= srcBounds.right); + + assert(destRect.top >= destBounds.top); + assert(destRect.bottom <= destBounds.bottom); + assert(destRect.left >= destBounds.left); + assert(destRect.right <= destBounds.right); + + const Region *mask = nullptr; + + if (maskRegion) + mask = *maskRegion; + + const Rect constrainedDestRect = mask ? destRect.Intersect(mask->rect) : destRect; if (!constrainedDestRect.IsValid()) return; - Rect constrainedSrcRect = *srcRect; - constrainedSrcRect.left += constrainedDestRect.left - destRect->left; - constrainedSrcRect.right += constrainedDestRect.right - destRect->right; - constrainedSrcRect.top += constrainedDestRect.top - destRect->top; - constrainedSrcRect.bottom += constrainedDestRect.bottom - destRect->bottom; + Rect constrainedSrcRect = srcRect; + constrainedSrcRect.left += constrainedDestRect.left - destRect.left; + constrainedSrcRect.right += constrainedDestRect.right - destRect.right; + constrainedSrcRect.top += constrainedDestRect.top - destRect.top; + constrainedSrcRect.bottom += constrainedDestRect.bottom - destRect.bottom; const size_t srcFirstCol = constrainedSrcRect.left - srcBitmap->m_rect.left; const size_t srcFirstRow = constrainedSrcRect.top - srcBitmap->m_rect.top; @@ -577,7 +629,7 @@ void CopyBits(const BitMap *srcBitmap, BitMap *destBitmap, const Rect *srcRect, const size_t destFirstCol = constrainedDestRect.left - destBitmap->m_rect.left; const size_t destFirstRow = constrainedDestRect.top - destBitmap->m_rect.top; - if (mask->size != sizeof(Region)) + if (mask && mask->size != sizeof(Region)) { PL_NotYetImplemented(); } @@ -608,8 +660,8 @@ void CopyBits(const BitMap *srcBitmap, BitMap *destBitmap, const Rect *srcRect, const size_t firstSrcByte = srcFirstRow * srcPitch + srcFirstCol * pixelSizeBytes; const size_t firstDestByte = destFirstRow * destPitch + destFirstCol * pixelSizeBytes; - const size_t numCopiedRows = srcRect->bottom - srcRect->top; - const size_t numCopiedCols = srcRect->right - srcRect->left; + const size_t numCopiedRows = srcRect.bottom - srcRect.top; + const size_t numCopiedCols = srcRect.right - srcRect.left; const size_t numCopiedBytesPerScanline = numCopiedCols * pixelSizeBytes; for (size_t i = 0; i < numCopiedRows; i++) @@ -617,11 +669,15 @@ void CopyBits(const BitMap *srcBitmap, BitMap *destBitmap, const Rect *srcRect, } } -void CopyMask(const BitMap *srcBitmap, const BitMap *maskBitmap, BitMap *destBitmap, const Rect *srcRect, const Rect *maskRect, const Rect *destRect) +void CopyBits(const BitMap *srcBitmap, BitMap *destBitmap, const Rect *srcRectBase, const Rect *destRectBase, CopyBitsMode copyMode, RgnHandle maskRegion) { - PL_NotYetImplemented(); + CopyBitsComplete(srcBitmap, nullptr, destBitmap, srcRectBase, nullptr, destRectBase, maskRegion); } +void CopyMask(const BitMap *srcBitmap, const BitMap *maskBitmap, BitMap *destBitmap, const Rect *srcRectBase, const Rect *maskRectBase, const Rect *destRectBase) +{ + CopyBitsComplete(srcBitmap, maskBitmap, destBitmap, srcRectBase, maskRectBase, destRectBase, nullptr); +} RgnHandle NewRgn() { @@ -725,8 +781,9 @@ void SubPt(Point srcPoint, Point *destPoint) Boolean SectRect(const Rect *rectA, const Rect *rectB, Rect *outIntersection) { - PL_NotYetImplemented(); - return false; + *outIntersection = rectA->Intersect(*rectB); + + return outIntersection->IsValid() ? PL_TRUE : PL_FALSE; } @@ -760,3 +817,15 @@ void BitMap::Init(const Rect &rect, GpPixelFormat_t pixelFormat, size_t pitch, v m_pitch = pitch; m_data = dataPtr; } + +#include "stb_image_write.h" + +void DebugPixMap(PixMap **pixMapH, const char *outName) +{ + PixMap *pixMap = *pixMapH; + char outPath[1024]; + strcpy_s(outPath, outName); + strcat_s(outPath, ".png"); + + stbi_write_png(outPath, pixMap->m_rect.right - pixMap->m_rect.left, pixMap->m_rect.bottom - pixMap->m_rect.top, 1, pixMap->m_data, pixMap->m_pitch); +} diff --git a/PortabilityLayer/PLQuickdraw.h b/PortabilityLayer/PLQuickdraw.h index 2ae2bc1..7c14a19 100644 --- a/PortabilityLayer/PLQuickdraw.h +++ b/PortabilityLayer/PLQuickdraw.h @@ -148,6 +148,7 @@ Pattern *GetQDGlobalsBlack(Pattern *pattern); // Index is 1-based void GetIndPattern(Pattern *pattern, int patListID, int index); +void DebugPixMap(PixMap **pixMap, const char *outName); void CopyBits(const BitMap *srcBitmap, BitMap *destBitmap, const Rect *srcRect, const Rect *destRect, CopyBitsMode copyMode, RgnHandle maskRegion); void CopyMask(const BitMap *srcBitmap, const BitMap *maskBitmap, BitMap *destBitmap, const Rect *srcRect, const Rect *maskRect, const Rect *destRect); diff --git a/PortabilityLayer/PortabilityLayer.vcxproj b/PortabilityLayer/PortabilityLayer.vcxproj index 2e78b00..9389171 100644 --- a/PortabilityLayer/PortabilityLayer.vcxproj +++ b/PortabilityLayer/PortabilityLayer.vcxproj @@ -60,21 +60,25 @@ + + + + @@ -241,6 +245,7 @@ + diff --git a/PortabilityLayer/PortabilityLayer.vcxproj.filters b/PortabilityLayer/PortabilityLayer.vcxproj.filters index e50e1cb..9c27b26 100644 --- a/PortabilityLayer/PortabilityLayer.vcxproj.filters +++ b/PortabilityLayer/PortabilityLayer.vcxproj.filters @@ -361,7 +361,7 @@ Header Files - Source Files + Header Files @@ -545,5 +545,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/PortabilityLayer/QDManager.cpp b/PortabilityLayer/QDManager.cpp index 6cc50f5..affa59b 100644 --- a/PortabilityLayer/QDManager.cpp +++ b/PortabilityLayer/QDManager.cpp @@ -6,6 +6,8 @@ #include "QDGraf.h" #include "QDState.h" +#include + namespace PortabilityLayer { class QDManagerImpl final : public QDManager @@ -20,6 +22,8 @@ namespace PortabilityLayer void DisposeGWorld(CGraf *gw) override; QDState *GetState() override; + int DepthForPixelFormat(GpPixelFormat_t pixelFormat) const override; + static QDManagerImpl *GetInstance(); private: @@ -59,6 +63,9 @@ namespace PortabilityLayer switch (depth) { + case 1: + pixelFormat = GpPixelFormats::kBW1; + break; case 8: pixelFormat = (colorTable == nullptr) ? GpPixelFormats::k8BitStandard : GpPixelFormats::k8BitCustom; break; @@ -72,9 +79,6 @@ namespace PortabilityLayer return genericErr; } - if (depth != 8) - return genericErr; - void *grafStorage = MemoryManager::GetInstance()->Alloc(sizeof(CGraf)); if (!grafStorage) return mFulErr; @@ -105,6 +109,25 @@ namespace PortabilityLayer return m_port->GetState(); } + int QDManagerImpl::DepthForPixelFormat(GpPixelFormat_t pixelFormat) const + { + switch (pixelFormat) + { + case GpPixelFormats::k8BitStandard: + case GpPixelFormats::k8BitCustom: + return 8; + case GpPixelFormats::kRGB555: + return 16; + case GpPixelFormats::kRGB24: + return 24; + case GpPixelFormats::kRGB32: + return 32; + default: + assert(false); + return 0; + } + } + QDManagerImpl *QDManagerImpl::GetInstance() { return &ms_instance; diff --git a/PortabilityLayer/QDManager.h b/PortabilityLayer/QDManager.h index 3535cb3..867032d 100644 --- a/PortabilityLayer/QDManager.h +++ b/PortabilityLayer/QDManager.h @@ -1,5 +1,7 @@ #pragma once +#include "GpPixelFormat.h" + struct ColorTable; struct CGraf; struct GDevice; @@ -21,6 +23,8 @@ namespace PortabilityLayer virtual QDState *GetState() = 0; + virtual int DepthForPixelFormat(GpPixelFormat_t pixelFormat) const = 0; + static QDManager *GetInstance(); }; } diff --git a/PortabilityLayer/QDPixMap.cpp b/PortabilityLayer/QDPixMap.cpp index 2ff3ab2..b459b58 100644 --- a/PortabilityLayer/QDPixMap.cpp +++ b/PortabilityLayer/QDPixMap.cpp @@ -42,7 +42,8 @@ namespace PortabilityLayer switch (pixelFormat) { case GpPixelFormats::k8BitCustom: - case GpPixelFormats::k8BitStandard: + case GpPixelFormats::k8BitStandard: + case GpPixelFormats::kBW1: rowByteCount = width; break; case GpPixelFormats::kRGB555: @@ -53,7 +54,7 @@ namespace PortabilityLayer break; case GpPixelFormats::kRGB32: rowByteCount = width * 4; - break; + break; default: assert(false); return 0; diff --git a/PortabilityLayer/QDStandardPalette.cpp b/PortabilityLayer/QDStandardPalette.cpp index 82ce866..cc04b23 100644 --- a/PortabilityLayer/QDStandardPalette.cpp +++ b/PortabilityLayer/QDStandardPalette.cpp @@ -150,7 +150,7 @@ namespace PortabilityLayer const int gGrayDelta = static_cast(g * 3) - static_cast(grayscaleTimes3); const int bGrayDelta = static_cast(b * 3) - static_cast(grayscaleTimes3); - if (rGrayDelta >= -3 && rGrayDelta <= 3 && gGrayDelta >= -3 && gGrayDelta <= -3 && bGrayDelta >= -3 && bGrayDelta <= -3) + if (rGrayDelta >= -3 && rGrayDelta <= 3 && gGrayDelta >= -3 && gGrayDelta <= 3 && bGrayDelta >= -3 && bGrayDelta <= 3) { // Divide down to 0..15 range const unsigned int grayscaleValue = (grayscaleTimes3 * 21 + 36) >> 6; @@ -167,7 +167,7 @@ namespace PortabilityLayer else if (grayscale6Step == 5) return 0; else - return 180 - 36 * grayscale6Step; + return 215 - 43 * grayscale6Step; } else return 255 - grayscale6Step * 2 - grayscale6StepRemainder; diff --git a/PortabilityLayer/WindowManager.cpp b/PortabilityLayer/WindowManager.cpp index 1b7c311..4b30d63 100644 --- a/PortabilityLayer/WindowManager.cpp +++ b/PortabilityLayer/WindowManager.cpp @@ -7,9 +7,11 @@ #include "PLCore.h" #include "PLEventQueue.h" #include "MemoryManager.h" +#include "MenuManager.h" #include "QDGraf.h" #include "QDManager.h" #include "QDPixMap.h" +#include "Vec2i.h" #include "WindowDef.h" struct GDevice; @@ -58,6 +60,7 @@ namespace PortabilityLayer void ShowWindow(Window *window) override; void HideWindow(Window *window) override; GDevice **GetWindowDevice(Window *window) override; + void FindWindow(const Point &point, Window **outWindow, short *outRegion) const override; void RenderFrame(IGpDisplayDriver *displayDriver) override; @@ -245,6 +248,60 @@ namespace PortabilityLayer return static_cast(window)->GetDevice(); } + void WindowManagerImpl::FindWindow(const Point &point, Window **outWindow, short *outRegion) const + { + // outRegion = One of: + /* + inMenuBar, + inSysWindow, + inContent, + inDrag, + inGrow, + inGoAway, + inZoomIn, + inZoomOut, + */ + + if (PortabilityLayer::MenuManager::GetInstance()->IsPointInMenuBar(PortabilityLayer::Vec2i(point.h, point.v))) + { + if (outWindow) + *outWindow = nullptr; + + if (outRegion) + *outRegion = inMenuBar; + + return; + } + + WindowImpl *window = m_windowStackTop; + while (window) + { + const Rect windowRect = window->m_graf.m_port.GetRect(); + + const int32_t localX = point.h - window->m_wmX; + const int32_t localY = point.v - window->m_wmY; + + if (localX >= 0 && localY >= 0 && localX < windowRect.right && localY < windowRect.bottom) + { + if (outWindow) + *outWindow = window; + + if (outRegion) + *outRegion = inContent; + + return; + } + + window = window->GetWindowBelow(); + } + + if (outWindow) + *outWindow = nullptr; + + if (outRegion) + *outRegion = 0; + } + void WindowManagerImpl::RenderFrame(IGpDisplayDriver *displayDriver) { GDevice **mainDeviceHdl = PortabilityLayer::DisplayDeviceManager::GetInstance()->GetMainDevice(); diff --git a/PortabilityLayer/WindowManager.h b/PortabilityLayer/WindowManager.h index 2fd188e..a8f1ae5 100644 --- a/PortabilityLayer/WindowManager.h +++ b/PortabilityLayer/WindowManager.h @@ -4,6 +4,8 @@ struct Window; struct CGraf; struct GDevice; struct IGpDisplayDriver; +struct Point; +struct Window; namespace PortabilityLayer { @@ -20,6 +22,7 @@ namespace PortabilityLayer virtual void ShowWindow(Window *window) = 0; virtual void HideWindow(Window *window) = 0; virtual GDevice **GetWindowDevice(Window *window) = 0; + virtual void FindWindow(const Point &point, Window **outWindow, short *outRegion) const = 0; virtual void RenderFrame(IGpDisplayDriver *displayDriver) = 0; diff --git a/README.txt b/README.txt index 8d19639..7eb489d 100644 --- a/README.txt +++ b/README.txt @@ -17,3 +17,12 @@ Other parts: - FTagData: Copies a data-only file to a .gpd and creates a .gpf for it - ImportCharSet: Imports the Unicode MacRoman description into a code page table. - PictChecker: Experimental app that extracts all of the PICT resources from all of the houses and dumps them to PNG. Used to verify that the PICT loader works. + + +GlidePort is intended to be primarily a straight port, retaining most of the look and feel of the original. + +Planned additions: +- 32-bit color support (mainly matters for houses that have higher-resolution PICT resources) +- Resolution changes while the game is running +- Bring back the gray-to-color intro fade that was removed in later versions +- Gamepad support