diff --git a/BasiliskII/src/CrossPlatform/video_blit.cpp b/BasiliskII/src/CrossPlatform/video_blit.cpp index 5f1a0ae3..b0da9593 100755 --- a/BasiliskII/src/CrossPlatform/video_blit.cpp +++ b/BasiliskII/src/CrossPlatform/video_blit.cpp @@ -245,7 +245,8 @@ static void Blit_Copy_Raw(uint8 * dest, const uint8 * source, uint32 length) (dst = (((src) >> 24) & UVAL64(0x000000ff000000ff)) | \ (((src) >> 8) & UVAL64(0x0000ff000000ff00)) | \ (((src) & UVAL64(0x0000ff000000ff00)) << 8) | \ - (((src) & UVAL64(0x000000ff000000ff)) << 24)) + (((src) & UVAL64(0x000000ff000000ff)) << 24) | \ + (UVAL64(0xff000000ff000000))) #define FB_DEPTH 24 #include "video_blit.h" diff --git a/BasiliskII/src/MacOSX/BasiliskII.xcodeproj/project.pbxproj b/BasiliskII/src/MacOSX/BasiliskII.xcodeproj/project.pbxproj index 8eb0925c..cc0b416f 100644 --- a/BasiliskII/src/MacOSX/BasiliskII.xcodeproj/project.pbxproj +++ b/BasiliskII/src/MacOSX/BasiliskII.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 28EFB21E2364DB4F00988A5B /* video_rootless.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28EFB21C2364DB4F00988A5B /* video_rootless.cpp */; }; + 28EFB222236CE16F00988A5B /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28EFB221236CE16F00988A5B /* QuartzCore.framework */; }; 752F26F91F240E51001032B4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 752F26F81F240E51001032B4 /* Foundation.framework */; }; 752F26FB1F240E69001032B4 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 752F26FA1F240E69001032B4 /* IOKit.framework */; }; 752F27011F242BAF001032B4 /* prefs_sdl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 752F27001F242BAF001032B4 /* prefs_sdl.cpp */; }; @@ -165,6 +167,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 28EFB21C2364DB4F00988A5B /* video_rootless.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = video_rootless.cpp; sourceTree = ""; }; + 28EFB221236CE16F00988A5B /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 752F26F81F240E51001032B4 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 752F26FA1F240E69001032B4 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; 752F27001F242BAF001032B4 /* prefs_sdl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = prefs_sdl.cpp; sourceTree = ""; }; @@ -399,6 +403,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 28EFB222236CE16F00988A5B /* QuartzCore.framework in Frameworks */, E413D93620D260DA00E437D8 /* SDL2.framework in Frameworks */, 756C1B391F25306A00620917 /* AppKit.framework in Frameworks */, 752F26FB1F240E69001032B4 /* IOKit.framework in Frameworks */, @@ -461,6 +466,7 @@ 752F26F71F240E51001032B4 /* Frameworks */ = { isa = PBXGroup; children = ( + 28EFB221236CE16F00988A5B /* QuartzCore.framework */, E413D93520D260DA00E437D8 /* SDL2.framework */, 756C1B381F25306A00620917 /* AppKit.framework */, 752F26FA1F240E69001032B4 /* IOKit.framework */, @@ -607,6 +613,7 @@ 75CBCF761F5DB65E00830063 /* video_sdl.cpp */, 75CBCF741F5DB3AD00830063 /* video_sdl2.cpp */, 752F27021F242F51001032B4 /* xpram_sdl.cpp */, + 28EFB21C2364DB4F00988A5B /* video_rootless.cpp */, ); name = SDL; path = ../SDL; @@ -1016,6 +1023,7 @@ 752F27011F242BAF001032B4 /* prefs_sdl.cpp in Sources */, 7539E2971F23C5FD006B2DF2 /* newcpu.cpp in Sources */, 7539E12A1F23B25A006B2DF2 /* vm_alloc.cpp in Sources */, + 28EFB21E2364DB4F00988A5B /* video_rootless.cpp in Sources */, E413D93220D260BC00E437D8 /* if.c in Sources */, 753253331F5368370024025B /* cpustbl_nf.cpp in Sources */, 7539E16C1F23B25A006B2DF2 /* main.cpp in Sources */, diff --git a/BasiliskII/src/MacOSX/config.h b/BasiliskII/src/MacOSX/config.h index 9b853b47..3fb0b3d8 100644 --- a/BasiliskII/src/MacOSX/config.h +++ b/BasiliskII/src/MacOSX/config.h @@ -816,5 +816,6 @@ /* #undef uintmax_t */ #define FPU_IEEE +#define VIDEO_ROOTLESS #endif diff --git a/BasiliskII/src/MacOSX/utils_macosx.mm b/BasiliskII/src/MacOSX/utils_macosx.mm index c68d2115..b2c66798 100644 --- a/BasiliskII/src/MacOSX/utils_macosx.mm +++ b/BasiliskII/src/MacOSX/utils_macosx.mm @@ -19,9 +19,12 @@ */ #include +#include +#include #include "sysdeps.h" #include "utils_macosx.h" #include +#include #if SDL_VERSION_ATLEAST(2,0,0) #include @@ -52,6 +55,79 @@ void disable_SDL2_macosx_menu_bar_keyboard_shortcuts() { } } +void make_window_transparent(SDL_Window * window) +{ + if (!window) { + return; + } + + extern int native_menubar_size; + native_menubar_size = (int)[[NSApp mainMenu] menuBarHeight]; + + SDL_SysWMinfo wmInfo; + SDL_VERSION(&wmInfo.version); + if (!SDL_GetWindowWMInfo(window, &wmInfo)) { + return; + } + + CGColorRef clearColor = [NSColor clearColor].CGColor; + NSWindow *cocoaWindow = wmInfo.info.cocoa.window; + NSView *sdlView = cocoaWindow.contentView; + sdlView.layer.backgroundColor = [NSColor clearColor].CGColor; + if (SDL_GetWindowData(window, "observing") == NULL) { + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserverForName:NSWindowDidBecomeKeyNotification object:cocoaWindow queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) { + NSWindow *window = (NSWindow*)note.object; + window.level = NSMainMenuWindowLevel+1; + }]; + [nc addObserverForName:NSWindowDidResignKeyNotification object:cocoaWindow queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) { + NSWindow *window = (NSWindow*)note.object; + // hack for window to be sent behind new key window + [window setIsVisible:NO]; + [window setLevel:NSNormalWindowLevel]; + [window setIsVisible:YES]; + }]; + SDL_SetWindowData(window, "observing", nc); + } + if (SDL_GetWindowData(window, "maskLayer") == NULL) { + CALayer *maskLayer = [CAShapeLayer layer]; + sdlView.layer.mask = maskLayer; + SDL_SetWindowData(window, "maskLayer", maskLayer); + } + cocoaWindow.backgroundColor = [NSColor clearColor]; + cocoaWindow.hasShadow = NO; + cocoaWindow.opaque = NO; + if (cocoaWindow.isKeyWindow) { + cocoaWindow.level = NSMainMenuWindowLevel+1; + } + + // make metal layer transparent + for (NSView *view in sdlView.subviews) { + if ([view.className isEqualToString:@"SDL_cocoametalview"]) { + view.layer.opaque = NO; + view.layer.backgroundColor = clearColor; + return; + } + } + + // make OpenGL surface transparent + GLint zero = 0; + [[NSOpenGLContext currentContext] setValues:&zero forParameter:NSOpenGLCPSurfaceOpacity]; +} + +void update_window_mask_rects(SDL_Window * window, int h, const std::vector &rects) +{ + CAShapeLayer *maskLayer = (CAShapeLayer*)SDL_GetWindowData(window, "maskLayer"); + CGMutablePathRef path = CGPathCreateMutable(); + for(auto it = rects.begin(); it != rects.end(); ++it) { + SDL_Rect rect = *it; + CGPathAddRect(path, NULL, CGRectMake(rect.x, rect.y, rect.w, rect.h)); + } + maskLayer.path = path; + maskLayer.affineTransform = CGAffineTransformScale(CGAffineTransformMakeTranslation(0, h), 1.0, -1.0); + CGPathRelease(path); +} + bool is_fullscreen_osx(SDL_Window * window) { if (!window) { diff --git a/BasiliskII/src/SDL/video_rootless.cpp b/BasiliskII/src/SDL/video_rootless.cpp new file mode 100644 index 00000000..6c667ba7 --- /dev/null +++ b/BasiliskII/src/SDL/video_rootless.cpp @@ -0,0 +1,517 @@ +// +// video_rootless.cpp +// BasiliskII +// +// Created by Jesús A. Álvarez on 26/10/2019. +// + +#include +#include "sysdeps.h" +#include "cpu_emulation.h" +#include "main.h" +#include "macos_util.h" +#include "prefs.h" +#include "emul_op.h" +#include + +extern void make_window_transparent(SDL_Window * window); +extern void update_sdl_video(SDL_Surface *s, int numrects, SDL_Rect *rects); +int native_menubar_size = 0; + +/* + * Rootless mode support + */ + +static const uint8 rootless_proc[] = { + // PEntryFromProcessSerialNumber(GetNextProcess(d0:d1)) + // returns next PSN in d0:d1, PEntryPtr in d2 + 0x2F, 0x01, // move.l d1,-(sp) ; psn low + 0x2F, 0x00, // move.l d0,-(sp) ; psn high + 0x55, 0x4F, // suba.w #2,sp ; result + 0x48, 0x6F, 0x00, 0x02, // pea 2(sp) ; ptr(psn) + 0x3F, 0x3C, 0x00, 0x38, // move.w #$38,-(sp) ; GetNextProcess + 0xA8, 0x8F, // _OSDispatch ; get psn of first process + 0x30, 0x1F, // move.w (sp)+,d0 ; result + + 0x59, 0x4F, // subq #4,sp ; result + 0x48, 0x6F, 0x00, 0x04, // pea 4(sp) ; ptr(psn) + 0x3F, 0x3C, 0x00, 0x4F, // move.w #$4F,-(sp) ; _PEntryFromProcessSerialNumber + 0xA8, 0x8F, // _OSDispatch ; get PEntry + 0x24, 0x1F, // move.l (sp)+, d2 ; pEntry + 0x20, 0x1F, // move.l (sp)+, d0 ; psn high + 0x22, 0x1F, // move.l (sp)+, d1 ; psn low + 0x4E, 0x75, // rts + + // a0 = GetResource(d0, d1) +#define GETRESOURCE 38 // offset to this function + 0x59, 0x4F, // subq #4,sp ; result + 0x2F, 0x00, // move.l d0,-(sp) ; type + 0x3F, 0x01, // move.w d1,-(sp) ; size + 0xA9, 0xA0, // _GetResource + 0x20, 0x57, // movea.l (sp),a0 ; result to a0 + 0x59, 0x4F, // subq #4, sp ; result + 0x2F, 0x08, // move.l a0,-(sp) ; handle + 0xA9, 0xA5, // _SizeRsrc + 0x20, 0x1F, // move.l (sp)+,d0 ; size + 0x20, 0x5F, // movea.l (sp)+,a0 ; handle + 0x4E, 0x75, // rts + +#define PAINTRGNPATCH (GETRESOURCE + 22) + M68K_EMUL_OP_PAINTRGN >> 8, + M68K_EMUL_OP_PAINTRGN & 0xff, + 0x2F, 0x3C, 0, 0, 0, 0, // move.l address, -(sp) + 0x4E, 0x75, // rts + +#define DRAGGRAYRGNPATCH (PAINTRGNPATCH + 10) + M68K_EMUL_OP_DRAGGRAYRGN >> 8, + M68K_EMUL_OP_DRAGGRAYRGN & 0xff, + 0x2F, 0x3C, 0, 0, 0, 0, // move.l address, -(sp) + 0x4E, 0x75, // rts +}; + +static uint32 rootless_proc_ptr = 0; +static uint32 low_mem_map = 0; +static bool dragging_region = false; +static uint32 drag_region_ptr = 0; + +static void MyPatchTrap(int trap, uint32 ptr) { + M68kRegisters r; + r.d[0] = trap; + Execute68kTrap(0xa746, &r); // GetToolTrapAddress() + WriteMacInt32(ptr + 4, r.a[0]); + r.d[0] = trap; + r.a[0] = ptr; + Execute68kTrap(0xa647, &r); // SetToolTrap() +} + +int16 InstallRootlessProc() { + // Rootless mode support + M68kRegisters r; + if (strncmp(PrefsFindString("screen"), "rootless", 8) == 0) { + printf("Installing rootless support\n"); + r.d[0] = sizeof(rootless_proc); + Execute68kTrap(0xa71e, &r); // NewPtrSysClear() + if (r.a[0] == 0) { + return memFullErr; + } + rootless_proc_ptr = r.a[0]; + Host2Mac_memcpy(rootless_proc_ptr, rootless_proc, sizeof(rootless_proc)); + low_mem_map = 0; + printf("Installed at 0x%x\n", rootless_proc_ptr); + + // Install patches to detect dragging regions + MyPatchTrap(0xa8d3, rootless_proc_ptr + PAINTRGNPATCH); + MyPatchTrap(0xa905, rootless_proc_ptr + DRAGGRAYRGNPATCH); + } else { + rootless_proc_ptr = 0; + low_mem_map = 0; + } + return noErr; +} + +static struct { + uint8_t *pixels; + uint8_t *cursorMask; + int w,h; +} display_mask = { + .pixels = NULL, + .cursorMask = NULL, + .w = 0, + .h = 0 +}; + +void MaskRect(int16 top, int16 left, int16 bottom, int16 right, bool in) { + if (top == bottom || left == right) return; + if (top < 0) top = 0; + if (left < 0) left = 0; + if (bottom > display_mask.h) bottom = display_mask.h; + if (right > display_mask.w) right = display_mask.w; + + uint8_t *line = display_mask.pixels + display_mask.w * top + left; + for (int y = top; y < bottom; y++) { + memset(line, in ? 0xff : 0x00, right - left); + line += display_mask.w; + } +} + +void PrintRegion(uint32 regionPtr) { + int16 size = ReadMacInt16(regionPtr); + int16 top = ReadMacInt16(regionPtr + 2); + int16 left = ReadMacInt16(regionPtr + 4); + int16 bottom = ReadMacInt16(regionPtr + 6); + int16 right = ReadMacInt16(regionPtr + 8); + printf("Region (%d: %d,%d %dx%d):\n", size, left, top, (right-left), (bottom-top)); + for(int i=0; i < size; i++) { + printf("%02x", ReadMacInt8(regionPtr + i)); + } + printf("\n"); +} + +void MaskRegion(uint32 regionPtr, bool in) { + // https://www.info-mac.org/viewtopic.php?t=17328 + uint16 size = ReadMacInt16(regionPtr); + int16 top = ReadMacInt16(regionPtr + 2); + int16 left = ReadMacInt16(regionPtr + 4); + int16 bottom = ReadMacInt16(regionPtr + 6); + int16 right = ReadMacInt16(regionPtr + 8); + if (bottom > display_mask.h) bottom = display_mask.h; + if (right > display_mask.w) right = display_mask.w; + + if (size == 10) { + MaskRect(top, left, bottom, right, in); + return; + } + + uint8_t *scanline = display_mask.pixels + top * display_mask.w; + uint8_t *curLine = (uint8*)alloca(display_mask.w); + memset(curLine, 0, display_mask.w); + + uint32 ptr = regionPtr + 10; + for (int16 y = top; y < bottom; y++) { + uint16 nextLine = ReadMacInt16(ptr); + if (nextLine == y) { + // apply changes to current line + ptr += 2; + for(;;) { + uint16 begin = ReadMacInt16(ptr); + ptr += 2; + if (begin == 0x7fff) break; + uint16 end = ReadMacInt16(ptr); + ptr += 2; + if (end > display_mask.w) { + end = display_mask.w; + } + for (int i=begin; i < end; i++) { + curLine[i] ^= 0xff; + } + } + } + + // blit current line + if (in) { + for (int x = left; x < right; x++) { + scanline[x] |= curLine[x]; + } + } else { + for (int x = left; x < right; x++) { + scanline[x] |= ~curLine[x]; + } + } + + scanline += display_mask.w; + } +} + +SDL_Rect GetRegionBounds(uint32 regionPtr) { + int16 top = ReadMacInt16(regionPtr + 2); + int16 left = ReadMacInt16(regionPtr + 4); + int16 bottom = ReadMacInt16(regionPtr + 6); + int16 right = ReadMacInt16(regionPtr + 8); + return (SDL_Rect){.x = left, .y = top, .w = right-left, .h = bottom-top}; +} + +uint32 GetLowMemOffset(uint32 addr) { + if (low_mem_map == 0) { + abort(); + } + + uint32 offset = 0; + uint32 ptr = low_mem_map; + for(;;) { + uint16 size = ReadMacInt16(ptr); + if (size == 0) break; + uint32 lo = ReadMacInt32(ptr+2); + ptr += 6; + if (addr < lo) { + return UINT32_MAX; + } else if (addr < (lo+size)) { + return offset + (addr-lo); + } + offset += size; + } + return UINT32_MAX; +} + +static uint32 GetResource(uint32 type, int16 id, int32* size) { + M68kRegisters r; + r.d[0] = type; + r.d[1] = id; + Execute68k(rootless_proc_ptr + GETRESOURCE, &r); + if (size) { + *size = r.d[0]; + } + return r.a[0]; +} + +static SDL_Rect MaskMenu(uint32 mbEntry) { + int16 menuTop = ReadMacInt16(mbEntry); + int16 menuLeft = ReadMacInt16(mbEntry + 2); + int16 menuBottom = ReadMacInt16(mbEntry + 4); + int16 menuRight = ReadMacInt16(mbEntry + 6); + MaskRect(menuTop-1, menuLeft-1, menuBottom+1, menuRight+1, true); + // shadow + MaskRect(menuBottom+1, menuLeft+1, menuBottom+2, menuRight+1, true); + MaskRect(menuTop+2, menuRight+1, menuBottom+2, menuRight+2, true); + return (SDL_Rect){.x = menuLeft-1, .y = menuTop-1, .w = menuRight - menuLeft + 3, .h = menuBottom - menuTop + 3}; +} + +uint16 menuEntries[16]; +uint16 *lastMenuEntry = menuEntries; +uint16 menuBarHeight; +bool inMenuSelect = false; + +static SDL_Rect MaskMenuBar() { + if (native_menubar_size && ReadMacInt16(0x0BAA) == 20) { + // Embiggen menubar + WriteMacInt16(0xBAA, native_menubar_size); + } + if (!inMenuSelect) { + menuBarHeight = ReadMacInt16(0x0BAA); + } + MaskRect(0, 0, menuBarHeight, display_mask.w, true); + return (SDL_Rect){.x = 0, .y = 0, .w = display_mask.w, .h = menuBarHeight}; +} + +static void MaskMenus(uint32 expandMem, uint32 lowMemPtr, std::vector &rects) { + uint32 mbSaveLoc = ReadMacInt32(ReadMacInt32(lowMemPtr + GetLowMemOffset(0x0B5C))); + if (mbSaveLoc == 0) { + // no menu yet + inMenuSelect = false; + return; + } + + uint16 mbEntryOffset = ReadMacInt16(mbSaveLoc); + if (mbEntryOffset == 0) { + inMenuSelect = false; + return; + } else if (lastMenuEntry == menuEntries && *lastMenuEntry == 0) { + // first menu + *lastMenuEntry = mbEntryOffset; + } else if (mbEntryOffset > *lastMenuEntry && lastMenuEntry < &menuEntries[16]) { + // added menu + *(++lastMenuEntry) = mbEntryOffset; + } else if (mbEntryOffset < *lastMenuEntry) { + // removed menu + lastMenuEntry--; + } + + inMenuSelect = true; + + // mask all menus + for (uint16 *entry = menuEntries; entry <= lastMenuEntry; entry++) { + rects.push_back(MaskMenu(mbSaveLoc + *entry)); + } +} + +static void MaskBits(int16 x, int16 y, uint16 bits) { + uint16 testBit = 0x8000; + for(int i=0; i < 16; i++, testBit >>= 1) { + if (x < 0 || y < 0 || y >= display_mask.h) continue; + display_mask.pixels[x + (y * display_mask.w) + i] |= (bits & testBit) ? 0xff : 0x00; + } +} + +bool cursor_point_opaque() { + if (display_mask.pixels == NULL) { + return true; + } + int32 my = ReadMacInt16(0x0828); + int32 mx = ReadMacInt16(0x082a); + return display_mask.cursorMask[mx + my * display_mask.w]; +} + +static SDL_Rect MaskCursor() { + int32 y = ReadMacInt16(0x0830); + int32 x = ReadMacInt16(0x0832); + // cursor data + uint16 *TheCrsr = (uint16*)Mac2HostAddr(0x0844); + // hotspot + uint16 hx = ntohs(TheCrsr[32]); + uint16 hy = ntohs(TheCrsr[33]); + + // apply mask + for (int i=0; i < 16; i++) { + MaskBits(x-hx, y+i-hy, ntohs(TheCrsr[16+i])); + } + return (SDL_Rect){.x=x-hx, .y=y-hy, .w=16, .h=16}; +} + +bool IsLayer(uint32 windowPtr) { + return ReadMacInt16(windowPtr + 0x4A) == 0xDEAD; +} + +void WalkLayerHierarchy(uint32 layerPtr, int level, std::vector &mask_rects) { + if (layerPtr == 0) return; + int kind = ReadMacInt16(layerPtr + 0x6C); + int visible = ReadMacInt8(layerPtr + 0x6E); + bool isLayer = IsLayer(layerPtr); + uint32 strucRgnHandle = ReadMacInt32(layerPtr + 0x72); + int x = 0,y = 0,w = 0,h = 0; + if (strucRgnHandle) { + uint32 regionPtr = ReadMacInt32(strucRgnHandle); + y = ReadMacInt16(regionPtr + 2); + x = ReadMacInt16(regionPtr + 4); + h = ReadMacInt16(regionPtr + 6) - y; + w = ReadMacInt16(regionPtr + 8) - x; + if (visible && w && h && !isLayer) { + mask_rects.push_back(GetRegionBounds(regionPtr)); + } + } + //printf("%*s%s 0x%x, kind=%d, visible=%d, %d,%d %dx%d\n", 2*level, "", IsLayer(layerPtr) ? "Layer" : "Window", layerPtr, kind, visible, x,y,w,h); + + if (IsLayer(layerPtr)) { + uint32 subWindows = ReadMacInt32(layerPtr + 0x94); + WalkLayerHierarchy(subWindows, level+1, mask_rects); + } + + uint32 nextWindow = ReadMacInt32(layerPtr + 0x90); + if (nextWindow) WalkLayerHierarchy(nextWindow, level, mask_rects); +} + +void update_display_mask(SDL_Window *window, int w, int h) { + if (rootless_proc_ptr == 0) { + return; + } + + // Check for process manager + uint32 expandMem = ReadMacInt32(0x02B6); + uint16 emProcessMgrExists = ReadMacInt16(expandMem + 0x0128); + if (!emProcessMgrExists) { + return; + } + + // Read lowmem mapping + if (low_mem_map == 0) { + uint32 handle = GetResource('lmem', -16458, NULL); + low_mem_map = ReadMacInt32(handle); + printf("low_mem_map at 0x%x\n", low_mem_map); + } + + std::vector mask_rects; + + if (display_mask.w != w || display_mask.h != h) { + // new mask + free(display_mask.pixels); + display_mask.pixels = (uint8_t*)calloc(2, w*h); + display_mask.w = w; + display_mask.h = h; + display_mask.cursorMask = &display_mask.pixels[display_mask.w * display_mask.h]; + + // update whole screen + make_window_transparent(window); + SDL_Rect rect = {.x = 0, .y = 0, .w = w, .h = h}; + update_sdl_video(NULL, 1, &rect); + mask_rects.push_back(rect); + memset(display_mask.pixels, 0xff, 2 * display_mask.w * display_mask.h); + } else { + // clear all + memset(display_mask.pixels, 0, display_mask.w * display_mask.h); + + // show non-desktop + uint32 deskPort = ReadMacInt32(0x9E2); + uint32 deskPortVisRgn = ReadMacInt32(ReadMacInt32(deskPort + 0x18)); + MaskRegion(deskPortVisRgn, false); + } + + bool has_front_process = false; + + M68kRegisters r; + uint32 rootLayerPtr = 0; + for(r.d[0] = 0, r.d[1] = 0;;) { + Execute68k(rootless_proc_ptr, &r); + uint32_t pEntryPtr = r.d[2]; + if (r.d[2] == 0) break; + + uint16 state = ReadMacInt16(pEntryPtr); + if (state == 4) { + has_front_process = true; + uint32 lowMemPtr = ReadMacInt32(ReadMacInt32(pEntryPtr + 0x9E)); + if (lowMemPtr) { + MaskMenus(expandMem, lowMemPtr, mask_rects); + } + } + + uint32 layerPtr = ReadMacInt32(pEntryPtr + 0x70); + uint16 layerTxSize = ReadMacInt16(layerPtr + 0x4A); + if (layerTxSize != 0xDEAD) { + // not a layer + continue; + } + + // find root layer + if (rootLayerPtr == 0) { + rootLayerPtr = ReadMacInt32(layerPtr + 0x82); // parent layer + while (ReadMacInt32(rootLayerPtr + 0x82)) { + rootLayerPtr = ReadMacInt32(rootLayerPtr + 0x82); + } + WalkLayerHierarchy(rootLayerPtr, 0, mask_rects); + } + } + + // Menu Bar + mask_rects.push_back(MaskMenuBar()); + + // Drag region + int8 mouseState = ReadMacInt8(0x0172); + if (dragging_region && mouseState) { + dragging_region = false; + drag_region_ptr = 0; + } else if (dragging_region && !mouseState && drag_region_ptr) { + MaskRegion(drag_region_ptr, true); + mask_rects.push_back(GetRegionBounds(drag_region_ptr)); + } + + // Copy over cursor mask + memcpy(display_mask.cursorMask, display_mask.pixels, display_mask.w * display_mask.h); + + // Cursor + if (cursor_point_opaque()) { + SDL_ShowCursor(SDL_DISABLE); + mask_rects.push_back(MaskCursor()); + } else { + SDL_ShowCursor(SDL_ENABLE); + } + + extern void update_window_mask_rects(SDL_Window * window, int h, const std::vector &rects); + update_window_mask_rects(window, display_mask.h, mask_rects); +} + +void apply_display_mask(SDL_Surface * host_surface, SDL_Rect update_rect) { + if (display_mask.pixels == NULL) { + return; + } + + if (host_surface->format->format != SDL_PIXELFORMAT_ARGB8888) { + printf("Invalid host surface\n"); + return; + } + + uint32_t * srcPixels = (uint32_t*)((uint8_t *)host_surface->pixels + + update_rect.y * host_surface->pitch + + update_rect.x * 4); + + uint8_t * srcMask = display_mask.pixels + update_rect.y * display_mask.w + update_rect.x; + for (int y = update_rect.y; y < update_rect.y+update_rect.h; y++) { + uint32_t * pixel = srcPixels; + uint8_t * mask = srcMask; + for (int x = update_rect.x; x < update_rect.x+update_rect.w; x++) { + if (*mask == 0) { + *pixel = 0; + } + pixel++; + mask++; + } + srcPixels += host_surface->pitch / 4; + srcMask += display_mask.w; + } +} + +void check_drag_region(M68kRegisters *r, uint16 opcode) { + if (opcode == M68K_EMUL_OP_DRAGGRAYRGN) { + dragging_region = true; + drag_region_ptr = 0; + } else if (opcode == M68K_EMUL_OP_PAINTRGN && dragging_region) { + uint32 regionHandle = ReadMacInt32(r->a[7]+4); + drag_region_ptr = ReadMacInt32(regionHandle); + } +} diff --git a/BasiliskII/src/SDL/video_sdl2.cpp b/BasiliskII/src/SDL/video_sdl2.cpp index 42247b21..8dec5feb 100644 --- a/BasiliskII/src/SDL/video_sdl2.cpp +++ b/BasiliskII/src/SDL/video_sdl2.cpp @@ -65,6 +65,12 @@ #include "video_blit.h" #include "vm_alloc.h" +#ifdef VIDEO_ROOTLESS +extern void update_display_mask(SDL_Window * window, int w, int h); +extern void apply_display_mask(SDL_Surface * host_surface, SDL_Rect update_rect); +extern bool cursor_point_opaque(void); +#endif + #define DEBUG 0 #include "debug.h" @@ -82,7 +88,8 @@ extern int display_type; // See enum above #else enum { DISPLAY_WINDOW, // windowed display - DISPLAY_SCREEN // fullscreen display + DISPLAY_SCREEN, // fullscreen display + DISPLAY_ROOTLESS // fullscreen with transparent desktop }; static int display_type = DISPLAY_WINDOW; // See enum above #endif @@ -737,6 +744,11 @@ static SDL_Surface * init_sdl_video(int width, int height, int bpp, Uint32 flags window_flags |= SDL_WINDOW_RESIZABLE; */ if (!sdl_window) { +#ifdef VIDEO_ROOTLESS + if (display_type == DISPLAY_ROOTLESS) { + window_flags |= SDL_WINDOW_BORDERLESS; + } +#endif sdl_window = SDL_CreateWindow( "Basilisk II", SDL_WINDOWPOS_UNDEFINED, @@ -750,7 +762,7 @@ static SDL_Surface * init_sdl_video(int width, int height, int bpp, Uint32 flags } } if (flags & SDL_WINDOW_FULLSCREEN) SDL_SetWindowGrab(sdl_window, SDL_TRUE); - + // Some SDL events (regarding some native-window events), need processing // as they are generated. SDL2 has a facility, SDL_AddEventWatch(), which // allows events to be processed as they are generated. @@ -894,6 +906,11 @@ static int present_sdl_video() } UNLOCK_PALETTE; // passed potential deadlock, can unlock palette +#ifdef VIDEO_ROOTLESS + // Apply mask + apply_display_mask(host_surface, sdl_update_video_rect); +#endif + // Update the host OS' texture void * srcPixels = (void *)((uint8_t *)host_surface->pixels + sdl_update_video_rect.y * host_surface->pitch + @@ -1369,6 +1386,13 @@ bool VideoInit(bool classic) display_type = DISPLAY_WINDOW; else if (sscanf(mode_str, "dga/%d/%d", &default_width, &default_height) == 2) display_type = DISPLAY_SCREEN; +#ifdef VIDEO_ROOTLESS + else if (strncmp(mode_str, "rootless", 8) == 0) { + display_type = DISPLAY_ROOTLESS; + default_width = sdl_display_width(); + default_height = sdl_display_height(); + } +#endif } if (default_width <= 0) default_width = sdl_display_width(); @@ -1458,7 +1482,18 @@ bool VideoInit(bool classic) for (int d = VIDEO_DEPTH_1BIT; d <= default_depth; d++) add_mode(display_type, w, h, video_modes[i].resolution_id, TrivialBytesPerRow(w, (video_depth)d), d); } - } +#ifdef VIDEO_ROOTLESS + } else if (display_type == DISPLAY_ROOTLESS) { + for (int i = 0; video_modes[i].w != 0; i++) { + const int w = video_modes[i].w; + const int h = video_modes[i].h; + if (i > 0 && (w >= default_width || h >= default_height)) + continue; + for (int d = VIDEO_DEPTH_1BIT; d <= default_depth; d++) + add_mode(display_type, w, h, video_modes[i].resolution_id, TrivialBytesPerRow(w, (video_depth)d), d); + } +#endif + } if (VideoModes.empty()) { ErrorAlert(STR_NO_XVISUAL_ERR); @@ -1697,6 +1732,9 @@ void VideoInterrupt(void) if (toggle_fullscreen) do_toggle_fullscreen(); +#ifdef VIDEO_ROOTLESS + update_display_mask(sdl_window, host_surface->w, host_surface->h); +#endif present_sdl_video(); // Temporarily give up frame buffer lock (this is the point where @@ -1931,132 +1969,31 @@ static bool is_hotkey_down(SDL_Keysym const & ks) static int kc_decode(SDL_Keysym const & ks, bool key_down) { + static int8_t usb_to_adb_scancode[] = { + -1, -1, -1, -1, 0, 11, 8, 2, 14, 3, 5, 4, 34, 38, 40, 37, + 46, 45, 31, 35, 12, 15, 1, 17, 32, 9, 13, 7, 16, 6, 18, 19, + 20, 21, 23, 22, 26, 28, 25, 29, 36, 53, 51, 48, 49, 27, 24, 33, + 30, 42, 42, 41, 39, 10, 43, 47, 44, 57, 122, 120, 99, 118, 96, 97, + 98, 100, 101, 109, 103, 111, 105, 107, 113, 114, 115, 116, 117, 119, 121, 60, + 59, 61, 62, 71, 75, 67, 78, 69, 76, 83, 84, 85, 86, 87, 88, 89, + 91, 92, 82, 65, 50, 55, 126, 81, 105, 107, 113, 106, 64, 79, 80, 90, + -1, -1, -1, -1, -1, 114, -1, -1, -1, -1, -1, -1, -1, -1, -1, 74, + 72, 73, -1, -1, -1, 95, -1, 94, -1, 93, -1, -1, -1, -1, -1, -1, + 104, 102, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 54, 56, 58, 55, 54, 56, 58, 55, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; switch (ks.sym) { - case SDLK_a: return 0x00; - case SDLK_b: return 0x0b; - case SDLK_c: return 0x08; - case SDLK_d: return 0x02; - case SDLK_e: return 0x0e; - case SDLK_f: return 0x03; - case SDLK_g: return 0x05; - case SDLK_h: return 0x04; - case SDLK_i: return 0x22; - case SDLK_j: return 0x26; - case SDLK_k: return 0x28; - case SDLK_l: return 0x25; - case SDLK_m: return 0x2e; - case SDLK_n: return 0x2d; - case SDLK_o: return 0x1f; - case SDLK_p: return 0x23; - case SDLK_q: return 0x0c; - case SDLK_r: return 0x0f; - case SDLK_s: return 0x01; - case SDLK_t: return 0x11; - case SDLK_u: return 0x20; - case SDLK_v: return 0x09; - case SDLK_w: return 0x0d; - case SDLK_x: return 0x07; - case SDLK_y: return 0x10; - case SDLK_z: return 0x06; - - case SDLK_1: case SDLK_EXCLAIM: return 0x12; - case SDLK_2: case SDLK_AT: return 0x13; - case SDLK_3: case SDLK_HASH: return 0x14; - case SDLK_4: case SDLK_DOLLAR: return 0x15; - case SDLK_5: return 0x17; - case SDLK_6: return 0x16; - case SDLK_7: return 0x1a; - case SDLK_8: return 0x1c; - case SDLK_9: return 0x19; - case SDLK_0: return 0x1d; - - case SDLK_BACKQUOTE: case 167: return 0x32; - case SDLK_MINUS: case SDLK_UNDERSCORE: return 0x1b; - case SDLK_EQUALS: case SDLK_PLUS: return 0x18; - case SDLK_LEFTBRACKET: return 0x21; - case SDLK_RIGHTBRACKET: return 0x1e; - case SDLK_BACKSLASH: return 0x2a; - case SDLK_SEMICOLON: case SDLK_COLON: return 0x29; - case SDLK_QUOTE: case SDLK_QUOTEDBL: return 0x27; - case SDLK_COMMA: case SDLK_LESS: return 0x2b; - case SDLK_PERIOD: case SDLK_GREATER: return 0x2f; - case SDLK_SLASH: case SDLK_QUESTION: return 0x2c; - case SDLK_TAB: if (is_hotkey_down(ks)) {if (!key_down) drv->suspend(); return -2;} else return 0x30; case SDLK_RETURN: if (is_hotkey_down(ks)) {if (!key_down) toggle_fullscreen = true; return -2;} else return 0x24; - case SDLK_SPACE: return 0x31; - case SDLK_BACKSPACE: return 0x33; - - case SDLK_DELETE: return 0x75; - case SDLK_INSERT: return 0x72; - case SDLK_HOME: case SDLK_HELP: return 0x73; - case SDLK_END: return 0x77; - case SDLK_PAGEUP: return 0x74; - case SDLK_PAGEDOWN: return 0x79; - - case SDLK_LCTRL: return 0x36; - case SDLK_RCTRL: return 0x36; - case SDLK_LSHIFT: return 0x38; - case SDLK_RSHIFT: return 0x38; -#ifdef __APPLE__ - case SDLK_LALT: return 0x3a; - case SDLK_RALT: return 0x3a; - case SDLK_LGUI: return 0x37; - case SDLK_RGUI: return 0x37; -#else - case SDLK_LALT: return 0x37; - case SDLK_RALT: return 0x37; - case SDLK_LGUI: return 0x3a; - case SDLK_RGUI: return 0x3a; -#endif - case SDLK_MENU: return 0x32; - case SDLK_CAPSLOCK: return 0x39; - case SDLK_NUMLOCKCLEAR: return 0x47; - - case SDLK_UP: return 0x3e; - case SDLK_DOWN: return 0x3d; - case SDLK_LEFT: return 0x3b; - case SDLK_RIGHT: return 0x3c; - case SDLK_ESCAPE: if (is_hotkey_down(ks)) {if (!key_down) { quit_full_screen = true; emerg_quit = true; } return -2;} else return 0x35; - case SDLK_F1: if (is_hotkey_down(ks)) {if (!key_down) SysMountFirstFloppy(); return -2;} else return 0x7a; - case SDLK_F2: return 0x78; - case SDLK_F3: return 0x63; - case SDLK_F4: return 0x76; - case SDLK_F5: return 0x60; - case SDLK_F6: return 0x61; - case SDLK_F7: return 0x62; - case SDLK_F8: return 0x64; - case SDLK_F9: return 0x65; - case SDLK_F10: return 0x6d; - case SDLK_F11: return 0x67; - case SDLK_F12: return 0x6f; - - case SDLK_PRINTSCREEN: return 0x69; - case SDLK_SCROLLLOCK: return 0x6b; - case SDLK_PAUSE: return 0x71; - - case SDLK_KP_0: return 0x52; - case SDLK_KP_1: return 0x53; - case SDLK_KP_2: return 0x54; - case SDLK_KP_3: return 0x55; - case SDLK_KP_4: return 0x56; - case SDLK_KP_5: return 0x57; - case SDLK_KP_6: return 0x58; - case SDLK_KP_7: return 0x59; - case SDLK_KP_8: return 0x5b; - case SDLK_KP_9: return 0x5c; - case SDLK_KP_PERIOD: return 0x41; - case SDLK_KP_PLUS: return 0x45; - case SDLK_KP_MINUS: return 0x4e; - case SDLK_KP_MULTIPLY: return 0x43; - case SDLK_KP_DIVIDE: return 0x4b; - case SDLK_KP_ENTER: return 0x4c; - case SDLK_KP_EQUALS: return 0x51; + default: return usb_to_adb_scancode[ks.scancode]; } - D(bug("Unhandled SDL keysym: %d\n", ks.sym)); - return -1; } static int event2keycode(SDL_KeyboardEvent const &ev, bool key_down) @@ -2162,6 +2099,11 @@ static void handle_events(void) // Mouse button case SDL_MOUSEBUTTONDOWN: { +#ifdef VIDEO_ROOTLESS + if (!cursor_point_opaque()) { + break; + } +#endif unsigned int button = event.button.button; if (button == SDL_BUTTON_LEFT) ADBMouseDown(0); diff --git a/BasiliskII/src/emul_op.cpp b/BasiliskII/src/emul_op.cpp index bdab4f63..bd5acd61 100644 --- a/BasiliskII/src/emul_op.cpp +++ b/BasiliskII/src/emul_op.cpp @@ -580,6 +580,16 @@ void EmulOp(uint16 opcode, M68kRegisters *r) #endif break; } + +#ifdef VIDEO_ROOTLESS + case M68K_EMUL_OP_PAINTRGN: + case M68K_EMUL_OP_DRAGGRAYRGN: { + // Patched traps for tracking dragged outlines + extern void check_drag_region(M68kRegisters *r, uint16 opcode); + check_drag_region(r, opcode); + break; + } +#endif default: printf("FATAL: EMUL_OP called with bogus opcode %08x\n", opcode); diff --git a/BasiliskII/src/include/emul_op.h b/BasiliskII/src/include/emul_op.h index e7684127..eb98e391 100644 --- a/BasiliskII/src/include/emul_op.h +++ b/BasiliskII/src/include/emul_op.h @@ -90,7 +90,9 @@ enum { M68K_EMUL_OP_SOUNDIN_CLOSE, M68K_EMUL_OP_DEBUGUTIL, M68K_EMUL_OP_IDLE_TIME, - M68K_EMUL_OP_SUSPEND, + M68K_EMUL_OP_SUSPEND, // 0x7138 + M68K_EMUL_OP_PAINTRGN, + M68K_EMUL_OP_DRAGGRAYRGN, M68K_EMUL_OP_MAX // highest number }; diff --git a/BasiliskII/src/video.cpp b/BasiliskII/src/video.cpp index 3b46864e..dd9310f5 100644 --- a/BasiliskII/src/video.cpp +++ b/BasiliskII/src/video.cpp @@ -482,6 +482,12 @@ int16 monitor_desc::driver_open(void) // Init color palette (solid gray) set_gray_palette(); + + // Enable rootless video +#ifdef VIDEO_ROOTLESS + extern int16 InstallRootlessProc(void); + return InstallRootlessProc(); +#endif return noErr; }