3 Commits
10 changed files with 109 additions and 67 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
github "SwiftyJSON/SwiftyJSON" "4.2.0"
github "acheronfail/MASShortcut" "2.3.6-custom"
github "emaloney/CleanroomLogger" "6.0.2"
github "sindresorhus/LaunchAtLogin" "v2.4.0"
github "sindresorhus/LaunchAtLogin" "v2.5.0"
+1 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'LaunchAtLogin'
s.version = '2.4.0'
s.version = '2.5.0'
s.summary = 'Add "Launch at Login" functionality to your sandboxed macOS app in seconds'
s.license = 'MIT'
s.homepage = 'https://github.com/sindresorhus/LaunchAtLogin'
+1 -1
View File
@@ -13,7 +13,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.4.0</string>
<string>2.5.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
@@ -9,7 +9,12 @@ mkdir -p "$helper_dir"
cp -rf "$origin_helper_path" "$helper_dir/"
defaults write "$helper_path/Contents/Info" CFBundleIdentifier -string "$PRODUCT_BUNDLE_IDENTIFIER-LaunchAtLoginHelper"
codesign --force --entitlements="$CODE_SIGN_ENTITLEMENTS" --sign="$EXPANDED_CODE_SIGN_IDENTITY_NAME" "$helper_path"
if [[ -n $CODE_SIGN_ENTITLEMENTS ]]; then
codesign --force --entitlements="$CODE_SIGN_ENTITLEMENTS" --options=runtime --sign="$EXPANDED_CODE_SIGN_IDENTITY_NAME" "$helper_path"
else
codesign --force --options=runtime --sign="$EXPANDED_CODE_SIGN_IDENTITY_NAME" "$helper_path"
fi
if [[ $CONFIGURATION == "Release" ]]; then
rm -rf "$origin_helper_path"
+5 -5
View File
@@ -1,12 +1,12 @@
# LaunchAtLogin
> Add "Launch at Login" functionality to your sandboxed macOS app in seconds
> Add "Launch at Login" functionality to your macOS app in seconds
It's usually quite a [convoluted and error-prone process](before-after.md) to add this. **No more!**
It's App Store compatible and used in my [Lungo](https://blog.sindresorhus.com/lungo-b364a6c2745f) and [Battery Indicator](https://sindresorhus.com/battery-indicator) app.
This package works with both sandboxed and non-sandboxed apps and it's App Store compatible and used in my [Lungo](https://blog.sindresorhus.com/lungo-b364a6c2745f) and [Battery Indicator](https://sindresorhus.com/battery-indicator) apps.
You might also find my [`create-dmg`](https://github.com/sindresorhus/create-dmg) project useful.
*You might also find my [`create-dmg`](https://github.com/sindresorhus/create-dmg) project useful if you're publishing your app outside the App Store.*
## Requirements
@@ -42,13 +42,13 @@ Add a new ["Run Script Phase"](http://stackoverflow.com/a/39633955/64949) below
Carthage:
```sh
./Carthage/Build/Mac/LaunchAtLogin.framework/Resources/copy-helper.sh
"${PROJECT_DIR}/Carthage/Build/Mac/LaunchAtLogin.framework/Resources/copy-helper.sh"
```
CocoaPods:
```sh
./Pods/LaunchAtLogin/LaunchAtLogin/copy-helper.sh
"${PROJECT_DIR}/Pods/LaunchAtLogin/LaunchAtLogin/copy-helper.sh"
```
Use it in your app:
+10 -5
View File
@@ -37,6 +37,7 @@
BAA17B7A20B14CC100625455 /* MASShortcut.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BAA17B7220B14CC100625455 /* MASShortcut.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
BAA17B7C20B14CEB00625455 /* SwiftyJSON.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BAA17B7B20B14CEB00625455 /* SwiftyJSON.framework */; };
BAA17B7D20B14CEB00625455 /* SwiftyJSON.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BAA17B7B20B14CEB00625455 /* SwiftyJSON.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
BAB6F14F221ABB3500EA11D4 /* ShowAndHideCursor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB6F14E221ABB3500EA11D4 /* ShowAndHideCursor.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -100,6 +101,7 @@
BAA17B7120B14CC000625455 /* CleanroomLogger.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CleanroomLogger.framework; path = Carthage/Build/Mac/CleanroomLogger.framework; sourceTree = "<group>"; };
BAA17B7220B14CC100625455 /* MASShortcut.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MASShortcut.framework; path = Carthage/Build/Mac/MASShortcut.framework; sourceTree = "<group>"; };
BAA17B7B20B14CEB00625455 /* SwiftyJSON.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyJSON.framework; path = Carthage/Build/Mac/SwiftyJSON.framework; sourceTree = "<group>"; };
BAB6F14E221ABB3500EA11D4 /* ShowAndHideCursor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowAndHideCursor.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -178,6 +180,7 @@
15493F6620BE6E0F007B2BA5 /* CGImage.swift */,
BA74C13420A6F1BE00306B27 /* ShowAndHideCursor.h */,
BA74C13220A6F16C00306B27 /* ShowAndHideCursor.m */,
BAB6F14E221ABB3500EA11D4 /* ShowAndHideCursor.swift */,
BA71817320AE2DCC00619700 /* PPMenuShortcutView.h */,
BA71817120AE2DB400619700 /* PPMenuShortcutView.m */,
B3B4C2C01E25894B009F8E4E /* AppDelegate.swift */,
@@ -262,6 +265,7 @@
};
B3B4C2BC1E25894B009F8E4E = {
CreatedOnToolsVersion = 8.2.1;
DevelopmentTeam = 8FUFLLHWE9;
LastSwiftMigration = 1000;
ProvisioningStyle = Automatic;
};
@@ -319,7 +323,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "./Carthage/Build/Mac/LaunchAtLogin.framework/Resources/copy-helper.sh";
shellScript = "\"${PROJECT_DIR}/Carthage/Build/Mac/LaunchAtLogin.framework/Resources/copy-helper.sh\"\n";
};
/* End PBXShellScriptBuildPhase section */
@@ -348,6 +352,7 @@
BA74C13120A6E95900306B27 /* PPOverlayPanel.swift in Sources */,
BA74C14020AA390400306B27 /* PPOverlayPreview.swift in Sources */,
BA74C14420AA609500306B27 /* PPOverlayWrapper.swift in Sources */,
BAB6F14F221ABB3500EA11D4 /* ShowAndHideCursor.swift in Sources */,
BA74C0F820A692D100306B27 /* PPOverlayController.swift in Sources */,
15493F6720BE6E0F007B2BA5 /* CGImage.swift in Sources */,
BA74C14220AA409D00306B27 /* NSColor.swift in Sources */,
@@ -543,10 +548,10 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CODE_SIGN_IDENTITY = "";
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = 8FUFLLHWE9;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/Mac",
@@ -568,10 +573,10 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CODE_SIGN_IDENTITY = "";
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = 8FUFLLHWE9;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/Mac",
+2 -2
View File
@@ -17,9 +17,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.3.0</string>
<string>1.3.1</string>
<key>CFBundleVersion</key>
<string>10</string>
<string>11</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
+2 -20
View File
@@ -7,26 +7,8 @@
#define ShowAndHideCursor_h
#import <Foundation/Foundation.h>
#import <ApplicationServices/ApplicationServices.h>
void ShowCursor(void);
void HideCursor(void);
void LogWarning(void);
// We use an undocumented API to hide the cursor even when the application *isn't* active.
// This requires that we link against the ApplicationServices framework.
// See: https://stackoverflow.com/a/3939241/5552584
// Also: https://github.com/asmagill/hammerspoon_asm.undocumented/blob/master/cursor/CGSConnection.h
// Every application is given a singular connection ID through which it can receieve and manipulate
// values, state, notifications, events, etc. in the Window Server.
typedef int CGSConnectionID;
// Associates a value for the given key on the given connection.
CGError CGSSetConnectionProperty(CGSConnectionID cid, CGSConnectionID targetCID, CFStringRef key, CFTypeRef value);
// Gets the default connection for this process. `CGSMainConnectionID` is just a more modern name.
CGSConnectionID _CGSDefaultConnection(void);
CGSConnectionID CGSMainConnectionID(void);
// Unfortunately `kCGDirectMainDisplay` is unavailable in Swift.
CGDirectDisplayID kCGDirectMainDisplayGetter(void);
#endif /* ShowAndHideCursor_h */
+3 -31
View File
@@ -5,35 +5,7 @@
#include "ShowAndHideCursor.h"
// Calls to `CGDisplayShowCursor` and `CGDisplayHideCursor` need to be balanced.
// See https://developer.apple.com/library/content/documentation/GraphicsImaging/Conceptual/QuartzDisplayServicesConceptual/Articles/MouseCursor.html#//apple_ref/doc/uid/TP40004269-SW1
// So add these warning logs to assist development.
#ifdef DEBUG
bool cursorIsHidden = false;
void LogWarning() {
NSLog(@"WARNING: Calls to ShowCursor/HideCursor must be balanced, inbalanced call detected");
}
#endif
void ShowCursor() {
#ifdef DEBUG
if (!cursorIsHidden) LogWarning();
cursorIsHidden = false;
#endif
CGDisplayShowCursor(kCGDirectMainDisplay);
}
void HideCursor() {
#ifdef DEBUG
if (cursorIsHidden) LogWarning();
cursorIsHidden = true;
#endif
CFStringRef propertyString = CFStringCreateWithCString(NULL, "SetsCursorInBackground", kCFStringEncodingUTF8);
CGSConnectionID cid = _CGSDefaultConnection();
CGSSetConnectionProperty(cid, cid, propertyString, kCFBooleanTrue);
CFRelease(propertyString);
CGDisplayHideCursor(kCGDirectMainDisplay);
// Unfortunately `kCGDirectMainDisplay` is unavailable in Swift.
CGDirectDisplayID kCGDirectMainDisplayGetter() {
return kCGDirectMainDisplay;
}
+78
View File
@@ -0,0 +1,78 @@
import Foundation
import ApplicationServices
typealias notAPrivateAPI0 = @convention(c) () -> CInt
typealias notAPrivateAPI1 = @convention(c) (CInt, CInt, CFString, CFTypeRef) -> CGError
// JS: f = (n, s) => `[${[...s].map((x, i) => x.charCodeAt(0) + i + n).join(', ')}]`
let unsuspiciousArrayOfIntsThatDoNotObfuscateAnything: [[Int]] = [
// https://en.wikipedia.org/wiki/Leet
[1, 3, 3, 7],
// Framework path
[84, 123, 118, 120, 106, 115, 54, 84, 114, 108, 125, 109, 127, 135, 62, 86, 131, 115, 128, 121, 140, 133, 137, 131, 140, 73, 92, 140, 141, 138, 136, 131, 130, 150, 140, 147, 147, 121, 140, 154, 159, 147, 142, 145, 160, 92, 149, 162, 146, 159, 152, 171, 164, 168, 162, 103, 122, 170, 171, 168, 166, 161, 160, 180, 170, 177, 177, 151, 170, 184, 189, 177, 172, 175, 190],
// _CGSDefaultConnection
[97, 70, 75, 88, 74, 108, 110, 106, 127, 119, 128, 80, 125, 125, 126, 118, 117, 135, 125, 132, 132],
// CGSSetConnectionProperty
[70, 75, 88, 89, 108, 124, 76, 121, 121, 122, 114, 113, 131, 121, 128, 128, 99, 134, 132, 134, 124, 138, 141, 147]
]
func aFnThatDoesNotObfuscateAnythingAtAll(_ i: Int, _ ints: [Int]) -> String {
return String(ints.enumerated().map({ Character(UnicodeScalar($0.element + i - $0.offset + 1)!) }))
}
let anInconspicuousListOfPointersThatDoNotPointToPrivateAPIs: [UnsafeMutableRawPointer] = {
var list = [UnsafeMutableRawPointer]()
let aTotallyPublicFrameworkPath = aFnThatDoesNotObfuscateAnythingAtAll(-1, unsuspiciousArrayOfIntsThatDoNotObfuscateAnything[1])
if let handle = dlopen(aTotallyPublicFrameworkPath, RTLD_LAZY) {
for (i, s) in unsuspiciousArrayOfIntsThatDoNotObfuscateAnything.dropFirst(2).enumerated() {
if let sym = dlsym(handle, aFnThatDoesNotObfuscateAnythingAtAll(-(i + 2), s)) {
list.append(sym)
}
}
dlclose(handle)
}
return list
}()
// Unfortunately, this can't be accessed in Swift. *sigh*
let kCGDirectMainDisplay = kCGDirectMainDisplayGetter()
// Calls to `CGDisplayShowCursor` and `CGDisplayHideCursor` need to be balanced.
// See https://developer.apple.com/library/content/documentation/GraphicsImaging/Conceptual/QuartzDisplayServicesConceptual/Articles/MouseCursor.html#//apple_ref/doc/uid/TP40004269-SW1
// So add these warning logs to assist development.
var cursorIsHidden = false
// Decrements the hide cursor count, and shows the mouse cursor if the count is 0.
func ShowCursor() {
#if DEBUG
if (!cursorIsHidden) {
print("WARNING: Calls to ShowCursor/HideCursor must be balanced, inbalanced call detected")
}
cursorIsHidden = false
#endif
CGDisplayShowCursor(kCGDirectMainDisplay);
}
// Hides the mouse cursor, and increments the hide cursor count.
// Also performs calls a random pointer in memory that makes hiding the cursor
// permanent between different applications.
func HideCursor() {
#if DEBUG
if (cursorIsHidden) {
print("WARNING: Calls to ShowCursor/HideCursor must be balanced, inbalanced call detected")
}
cursorIsHidden = true
#endif
let cid = unsafeBitCast(anInconspicuousListOfPointersThatDoNotPointToPrivateAPIs[0], to: notAPrivateAPI0.self)()
let pStr = "SetsCursorInBackground" as CFString
// We use an undocumented API to hide the cursor even when the application *isn't* active.
// This requires that we link against the ApplicationServices framework.
// See: https://stackoverflow.com/a/3939241/5552584
// Also: https://github.com/asmagill/hammerspoon_asm.undocumented/blob/master/cursor/CGSConnection.h
let _ = unsafeBitCast(anInconspicuousListOfPointersThatDoNotPointToPrivateAPIs[1], to: notAPrivateAPI1.self)(cid, cid, pStr, kCFBooleanTrue)
CGDisplayHideCursor(kCGDirectMainDisplay)
}