From 2ce5ef0a83e7f1619feccac67165a778175d8bb5 Mon Sep 17 00:00:00 2001 From: Wenuja Liyanamana Date: Sat, 8 Aug 2026 21:29:05 +0530 Subject: [PATCH 1/2] remove unnecessary .DS_Store and UserInterfaceState.xcuserstate files --- .DS_Store | Bin 10244 -> 0 bytes .../UserInterfaceState.xcuserstate | Bin 24952 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store delete mode 100644 Purrl.xcodeproj/project.xcworkspace/xcuserdata/wenujaliyanamana.xcuserdatad/UserInterfaceState.xcuserstate diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 63de18b2d6f981e779f4be91b3d6fbf494770e4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10244 zcmeHNeQX@X6@PE-gk7@4Yhwoza`Gh?LXmpBu|NWa{IMhLzL_`k-pqUVe)D#A4*)P-F?s;v03g%FsHW2G3WfEvYgv&(&RU{K z@&G=#Pyr7LU@jXSF(MB{9*8^;c_8vYxn#OY!* z71BjP`PM-F-bQPL?+4;YGKc*H;GHIwdeQzoL|n{Zl3y|%a?7- z50c5(QEF;lwfZ_)meI`ryH226j_Gj$ z%P?uk zpE_-q9QQF{3x@jv+|Xd#<0~0v`?LMRu%=Aaus5oPDc7%2Z-|ZM#wXOtoKqc=&F`nC2u`gv4VLmA}*yI`x^V_k}-%UPcsBvf>(J&NWl+0zsULbO-CL)Hvs)YkK+Em(J|`(^E@GBztL z7tD6`pseHyZmC4Z_=1;?4anNd^8IY$A>9v#%cfIZQaW}-mM8of-7|;hCfC-_tRIX;8u@HhB8p2rvQ6?_%{h=0O2@jv)BzJr(WL;MIomRh9^(iZ7fX{*#N zC8gcc?b5x{VQEAfm-13knh_RARoI=Xw@jL+_7(RNf?c?uNawB$yUxQ$2Jcgcu6fw~ zbu9zhjqM%lZi?T!y}Nhcfxe}16wzA^*jga)*sntjk9{5=jd3dwsdkPMILtFE0aLq< zfmpuCMsM1zc515#KBBqxHfpXW=!oWyuFg(PBE*VjvZqtknh1{ym)+AvO$-}%-leKq zGa*y--4mlGgpG7RsbM&izvzhnGrR$B!P{^N-pAFr1~u%&ZMX}2DZ=l?BNXLBc#NVv zhZA@bC(**FRc{Ovl-8^U8hL>3=OMbgunatz#g>SLYO<* z<7k?$T_5v8wYrftS@wJt6<00Gu3uQ_xuch|>)zcg)AkVUitpW*Y{<0V;){kAGwl=g zCRv3l(DAjZNq^V0x(q82)Y{(K(LpGp0N${1O1m+y7fu&F_DDb((e=`H#a!A9YI0~> z!?@$7asOpjcD_sryZthQ>pJY2rgO6BS;p}6v~LEvn)>+ij6KQoePEUbooV-qS$LUD zZd~^~-6@*s`BOZF4LfN!2;34+GIdW6eC|wHFHlZS9DG-}H<22tRxTP{aG?1D4&1<0kRN)yWTHOPer8W8a;>8eG< zr%2nyo(v`YGG7XAe23_#NR-@xfe?Z6`Tu7By|v}#zRx0MEJ;D{FNQvwRC%U$b&G6~ z3LGcfb^cs!l0Qmd)O!Fo)2LbxFsZ0S9*8^;dEjb!ph-*=XYc^P`TsQ{Qn6A$H>Y6 diff --git a/Purrl.xcodeproj/project.xcworkspace/xcuserdata/wenujaliyanamana.xcuserdatad/UserInterfaceState.xcuserstate b/Purrl.xcodeproj/project.xcworkspace/xcuserdata/wenujaliyanamana.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index 021320b58b68e2b3876ff4492150c06076622f0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24952 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 From 23c079a00d7ed0089bf26d6c4ab8393b92371c0a Mon Sep 17 00:00:00 2001 From: Wenuja Liyanamana Date: Sat, 8 Aug 2026 21:29:12 +0530 Subject: [PATCH 2/2] Enhance scroll functionality with only scrollable content option and add ScrollabilityDetector class --- .gitignore | 16 +++++ Purrl.icns | Bin 23120 -> 0 bytes Purrl.xcodeproj/project.pbxproj | 18 +++-- Purrl/AppDelegate.swift | 15 ++++ Purrl/ScrollHapticEngine.swift | 10 +++ Purrl/ScrollabilityDetector.swift | 110 +++++++++++++++++++++++++++++ Purrl/en.lproj/Localizable.strings | 2 + Purrl/es.lproj/Localizable.strings | 2 + build.sh | 1 + project.xml | 20 ++++-- 10 files changed, 182 insertions(+), 12 deletions(-) delete mode 100644 Purrl.icns create mode 100644 Purrl/ScrollabilityDetector.swift diff --git a/.gitignore b/.gitignore index 03c5975..25d4b4f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,19 @@ +# macOS system files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes + +# Xcode user data & derived data build/ +DerivedData/ +*.xcodeproj/xcuserdata/ +*.xcodeproj/project.xcworkspace/xcuserdata/ +*.xcworkspace/xcuserdata/ +*.xcuserstate + +# Temporary scratch & release build artifacts +scratch/ dmg-source/ *.dmg diff --git a/Purrl.icns b/Purrl.icns deleted file mode 100644 index cf3d1ee3dc30ad706761da4373cdb9eab43230b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23120 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 0 else { return } diff --git a/Purrl/ScrollabilityDetector.swift b/Purrl/ScrollabilityDetector.swift new file mode 100644 index 0000000..0d1360b --- /dev/null +++ b/Purrl/ScrollabilityDetector.swift @@ -0,0 +1,110 @@ +// +// ScrollabilityDetector.swift +// Purrl +// +// Created by Purrl on 08/08/2026. +// + +import Cocoa +import ApplicationServices + +class ScrollabilityDetector { + private var cachedLocation: CGPoint = .zero + private var cachedIsScrollable: Bool = true + private var cachedTime: TimeInterval = 0 + private let cacheDuration: TimeInterval = 0.04 // 40ms cache for instant responsiveness + private let cacheDistanceThreshold: CGFloat = 1.0 // 1 point movement threshold + + /// Returns whether the UI element under the current mouse cursor is in a scrollable view or region. + func isScrollableAtCurrentCursor() -> Bool { + guard let cgEvent = CGEvent(source: nil) else { return true } + let point = cgEvent.location + let now = ProcessInfo.processInfo.systemUptime + + // Use cached decision if cursor location hasn't moved and cache is fresh + let distance = hypot(point.x - cachedLocation.x, point.y - cachedLocation.y) + if distance < cacheDistanceThreshold && (now - cachedTime) < cacheDuration { + return cachedIsScrollable + } + + let isScrollable = checkScrollability(at: point) + + cachedLocation = point + cachedTime = now + cachedIsScrollable = isScrollable + return isScrollable + } + + private func checkScrollability(at point: CGPoint) -> Bool { + // If process is not trusted for Accessibility, default to true + guard AXIsProcessTrusted() else { return true } + + let systemWide = AXUIElementCreateSystemWide() + var elementRef: AXUIElement? + let result = AXUIElementCopyElementAtPosition(systemWide, Float(point.x), Float(point.y), &elementRef) + + guard result == .success, let startElement = elementRef else { + return true + } + + var current: AXUIElement? = startElement + var depth = 0 + let maxDepth = 15 + + // Recognized scrollable container roles in macOS Accessibility + let scrollableRoles: Set = [ + kAXScrollAreaRole, + "AXScrollView", + "AXWebArea", + kAXTableRole, + kAXOutlineRole, + kAXListRole, + kAXTextAreaRole, + kAXScrollBarRole, + "AXGrid", + "AXBrowser" + ] + + while let elem = current, depth < maxDepth { + // 1. Inspect role, subrole, and title + var roleRef: CFTypeRef? + AXUIElementCopyAttributeValue(elem, kAXRoleAttribute as CFString, &roleRef) + let roleString = (roleRef as? String) ?? "" + + var subroleRef: CFTypeRef? + AXUIElementCopyAttributeValue(elem, kAXSubroleAttribute as CFString, &subroleRef) + let subroleString = (subroleRef as? String) ?? "" + + var titleRef: CFTypeRef? + AXUIElementCopyAttributeValue(elem, kAXTitleAttribute as CFString, &titleRef) + let titleString = (titleRef as? String) ?? "" + + // Non-scrollable system regions (Menu bar, Status bar, Dock) + if roleString == kAXMenuBarRole || roleString == "AXMenuExtra" || roleString == kAXMenuRole || roleString == kAXMenuItemRole || subroleString == "AXDockItem" { + return false + } + + // Finder Desktop background check + if subroleString == "AXDesktopArea" || (roleString == kAXScrollAreaRole && titleString == "Desktop") { + return false + } + + // If ancestor is a valid scrollable container + if scrollableRoles.contains(roleString) { + return true + } + + // Inspect parent element up the hierarchy + var parentRef: CFTypeRef? + let parentResult = AXUIElementCopyAttributeValue(elem, kAXParentAttribute as CFString, &parentRef) + if parentResult == .success, let parent = parentRef { + current = (parent as! AXUIElement) + } else { + current = nil + } + depth += 1 + } + + return false + } +} diff --git a/Purrl/en.lproj/Localizable.strings b/Purrl/en.lproj/Localizable.strings index cad164b..3fe0e92 100644 --- a/Purrl/en.lproj/Localizable.strings +++ b/Purrl/en.lproj/Localizable.strings @@ -6,6 +6,8 @@ "toggle.enabled" = "Enabled ✓"; "toggle.disabled" = "Disabled"; "menu.texture" = "Purr texture"; +"menu.onlyScrollable" = "Only Scrollable Content"; +"menu.onlyScrollableEnabled" = "Only Scrollable Content ✓"; "menu.launchAtLogin" = "Launch at Login"; "menu.launchAtLoginEnabled" = "Launch at Login ✓"; "menu.checkForUpdates" = "Check for Updates…"; diff --git a/Purrl/es.lproj/Localizable.strings b/Purrl/es.lproj/Localizable.strings index 91ded45..90d1bb5 100644 --- a/Purrl/es.lproj/Localizable.strings +++ b/Purrl/es.lproj/Localizable.strings @@ -6,6 +6,8 @@ "toggle.enabled" = "Activado ✓"; "toggle.disabled" = "Desactivado"; "menu.texture" = "Textura del scroll"; +"menu.onlyScrollable" = "Solo contenido desplazable"; +"menu.onlyScrollableEnabled" = "Solo contenido desplazable ✓"; "menu.launchAtLogin" = "Iniciar con el sistema"; "menu.launchAtLoginEnabled" = "Iniciar con el sistema ✓"; "menu.checkForUpdates" = "Buscar actualizaciones…"; diff --git a/build.sh b/build.sh index 57c04f6..3cfaefd 100755 --- a/build.sh +++ b/build.sh @@ -22,6 +22,7 @@ rm -f "$ICNS_NAME" echo "==> Building $APP_NAME (Release)..." xcodebuild -scheme "$SCHEME" -configuration "$CONFIGURATION" \ -derivedDataPath "$DERIVED_DATA_PATH" \ + CODE_SIGN_IDENTITY="-" DEVELOPMENT_TEAM="" \ clean build APP_PATH="$DERIVED_DATA_PATH/Build/Products/$CONFIGURATION/$APP_NAME.app" diff --git a/project.xml b/project.xml index 2a69a4b..5978c51 100644 --- a/project.xml +++ b/project.xml @@ -274,8 +274,10 @@ YES DEBUG_INFORMATION_FORMAT dwarf + CODE_SIGN_IDENTITY + - DEVELOPMENT_TEAM - 67NY9CT7W7 + ENABLE_STRICT_OBJC_MSGSEND YES ENABLE_TESTABILITY @@ -403,8 +405,10 @@ YES DEBUG_INFORMATION_FORMAT dwarf-with-dsym + CODE_SIGN_IDENTITY + - DEVELOPMENT_TEAM - 67NY9CT7W7 + ENABLE_NS_ASSERTIONS NO ENABLE_STRICT_OBJC_MSGSEND @@ -470,15 +474,17 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME AccentColor CODE_SIGN_STYLE - Automatic + Manual COMBINE_HIDPI_IMAGES YES CURRENT_PROJECT_VERSION 1 DEAD_CODE_STRIPPING YES + CODE_SIGN_IDENTITY + - DEVELOPMENT_TEAM - 3T9J355KZF + ENABLE_APP_SANDBOX NO ENABLE_HARDENED_RUNTIME @@ -533,15 +539,17 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME AccentColor CODE_SIGN_STYLE - Automatic + Manual COMBINE_HIDPI_IMAGES YES CURRENT_PROJECT_VERSION 1 DEAD_CODE_STRIPPING YES + CODE_SIGN_IDENTITY + - DEVELOPMENT_TEAM - 3T9J355KZF + ENABLE_APP_SANDBOX NO ENABLE_HARDENED_RUNTIME