Skip to content
This repository has been archived by the owner on Mar 6, 2024. It is now read-only.

Commit

Permalink
Require macOS 10.15
Browse files Browse the repository at this point in the history
  • Loading branch information
sindresorhus committed Feb 10, 2020
1 parent 44c2735 commit 2ecf880
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 32 deletions.
14 changes: 8 additions & 6 deletions Touch Bar Simulator.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,7 @@
TargetAttributes = {
E3FE2CBE1E726CE800C6713A = {
CreatedOnToolsVersion = 8.2.1;
DevelopmentTeam = YG56YK5RN5;
LastSwiftMigration = 1020;
ProvisioningStyle = Automatic;
SystemCapabilities = {
com.apple.HardenedRuntime = {
enabled = 0;
Expand Down Expand Up @@ -327,7 +325,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14.4;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
Expand Down Expand Up @@ -381,7 +379,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14.4;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
Expand All @@ -396,7 +394,8 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = YG56YK5RN5;
Expand All @@ -419,6 +418,7 @@
MARKETING_VERSION = 3.2.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.sindresorhus.Touch-Bar-Simulator";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
REEXPORTED_LIBRARY_PATHS = "";
SWIFT_COMPILATION_MODE = singlefile;
SWIFT_OBJC_BRIDGING_HEADER = "Touch Bar Simulator/Touch Bar Simulator-Bridging-Header.h";
Expand All @@ -435,7 +435,8 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = YG56YK5RN5;
Expand All @@ -458,6 +459,7 @@
MARKETING_VERSION = 3.2.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.sindresorhus.Touch-Bar-Simulator";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
REEXPORTED_LIBRARY_PATHS = "";
SWIFT_OBJC_BRIDGING_HEADER = "Touch Bar Simulator/Touch Bar Simulator-Bridging-Header.h";
SYSTEM_FRAMEWORK_SEARCH_PATHS = (
Expand Down
10 changes: 7 additions & 3 deletions Touch Bar Simulator/TouchBarView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@ final class TouchBarView: NSView {
override func acceptsFirstMouse(for event: NSEvent?) -> Bool { true }

func start() {
stream = SLSDFRDisplayStreamCreate(0, .main) { status, _, frameSurface, _ in
guard status == .frameComplete else {
stream = SLSDFRDisplayStreamCreate(0, .main) { [weak self] status, _, frameSurface, _ in
guard
let self = self,
status == .frameComplete,
let layer = self.layer
else {
return
}

self.layer!.contents = frameSurface
layer.contents = frameSurface
}.takeUnretainedValue()

DFRSetStatus(2)
Expand Down
42 changes: 24 additions & 18 deletions Touch Bar Simulator/TouchBarWindow.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Cocoa
import Combine
import Defaults

final class TouchBarWindow: NSPanel {
Expand Down Expand Up @@ -35,12 +36,11 @@ final class TouchBarWindow: NSPanel {
}

override var canBecomeMain: Bool { false }

override var canBecomeKey: Bool { false }

var docking: Docking = .floating {
didSet {
if oldValue == .floating && docking != .floating {
if oldValue == .floating, docking != .floating {
Defaults[.lastFloatingPosition] = frame.origin
}

Expand All @@ -66,11 +66,6 @@ final class TouchBarWindow: NSPanel {
}
}

@objc
func didChangeScreenParameters(_ notification: Notification) {
docking.reposition(window: self)
}

var showOnAllDesktops: Bool = false {
didSet {
if showOnAllDesktops {
Expand Down Expand Up @@ -171,7 +166,11 @@ final class TouchBarWindow: NSPanel {
return
}

showTouchBarTimer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: false) { _ in
showTouchBarTimer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: false) { [weak self] _ in
guard let self = self else {
return
}

self.performActionWithAnimation(action: .show)
self.showAnimationDidRun = true
}
Expand All @@ -180,7 +179,7 @@ final class TouchBarWindow: NSPanel {
showTouchBarTimer = Timer()
showAnimationDidRun = false

if isVisible && !dismissAnimationDidRun {
if isVisible, !dismissAnimationDidRun {
performActionWithAnimation(action: .dismiss)
dismissAnimationDidRun = true
}
Expand Down Expand Up @@ -333,6 +332,7 @@ final class TouchBarWindow: NSPanel {
}

private var defaultsObservations: [DefaultsObservation] = []
private var cancellable: AnyCancellable?

func setUp() {
let view = contentView!
Expand All @@ -345,22 +345,22 @@ final class TouchBarWindow: NSPanel {
view.addSubview(touchBarView)

// TODO: These could use the `observe` method with `tiedToLifetimeOf` so we don't have to manually invalidate them.
defaultsObservations.append(Defaults.observe(.windowTransparency) { change in
self.alphaValue = CGFloat(change.newValue)
defaultsObservations.append(Defaults.observe(.windowTransparency) { [weak self] change in
self?.alphaValue = CGFloat(change.newValue)
})

defaultsObservations.append(Defaults.observe(.windowDocking) { change in
self.docking = change.newValue
defaultsObservations.append(Defaults.observe(.windowDocking) { [weak self] change in
self?.docking = change.newValue
})

// TODO: We could maybe simplify this by creating another `Default` extension to bind a default to a KeyPath:
// `defaults.bind(.showOnAllDesktops, to: \.showOnAllDesktop)`
defaultsObservations.append(Defaults.observe(.showOnAllDesktops) { change in
self.showOnAllDesktops = change.newValue
defaultsObservations.append(Defaults.observe(.showOnAllDesktops) { [weak self] change in
self?.showOnAllDesktops = change.newValue
})

defaultsObservations.append(Defaults.observe(.dockBehavior) { change in
self.dockBehavior = change.newValue
defaultsObservations.append(Defaults.observe(.dockBehavior) { [weak self] change in
self?.dockBehavior = change.newValue
})

center()
Expand All @@ -374,7 +374,13 @@ final class TouchBarWindow: NSPanel {
orderFront(nil)
}

NotificationCenter.default.addObserver(self, selector: #selector(didChangeScreenParameters(_:)), name: NSApplication.didChangeScreenParametersNotification, object: nil)
cancellable = NSScreen.publisher.sink { [weak self] in
guard let self = self else {
return
}

self.docking.reposition(window: self)
}
}

deinit {
Expand Down
52 changes: 52 additions & 0 deletions Touch Bar Simulator/util.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Cocoa
import Combine

/**
Convenience function for initializing an object and modifying its properties.
Expand All @@ -18,6 +19,7 @@ func with<T>(_ item: T, update: (inout T) throws -> Void) rethrows -> T {
return this
}


extension CGRect {
func adding(padding: Double) -> Self {
Self(
Expand All @@ -41,10 +43,12 @@ extension CGRect {
}
}


extension NSWindow {
var toolbarView: NSView? { standardWindowButton(.closeButton)?.superview }
}


extension NSWindow {
enum MoveXPositioning {
case left, center, right
Expand Down Expand Up @@ -87,12 +91,14 @@ extension NSWindow {
}
}


extension NSView {
func addSubviews(_ subviews: NSView...) {
subviews.forEach { addSubview($0) }
}
}


extension NSMenuItem {
var isChecked: Bool {
get { state == .on }
Expand All @@ -102,6 +108,7 @@ extension NSMenuItem {
}
}


extension NSMenuItem {
convenience init(
_ title: String,
Expand All @@ -124,6 +131,7 @@ extension NSMenuItem {
}
}


final class AssociatedObject<T: Any> {
subscript(index: Any) -> T? {
get {
Expand All @@ -134,6 +142,7 @@ final class AssociatedObject<T: Any> {
}
}


@objc
protocol TargetActionSender: AnyObject {
var target: AnyObject? { get set }
Expand Down Expand Up @@ -203,11 +212,13 @@ extension TargetActionSender {
}
}


extension NSApplication {
var isLeftMouseDown: Bool { currentEvent?.type == .leftMouseDown }
var isOptionKeyDown: Bool { NSEvent.modifierFlags.contains(.option) }
}


// TODO: Find a namespace to put this onto. I don't like free-floating functions.
func pressKey(keyCode: CGKeyCode, flags: CGEventFlags = []) {
let eventSource = CGEventSource(stateID: .hidSystemState)
Expand All @@ -218,6 +229,7 @@ func pressKey(keyCode: CGKeyCode, flags: CGEventFlags = []) {
keyUp?.post(tap: .cghidEventTap)
}


extension NSWindow.Level {
private static func level(for cgLevelKey: CGWindowLevelKey) -> NSWindow.Level {
NSWindow.Level(rawValue: Int(CGWindowLevelForKey(cgLevelKey)))
Expand All @@ -236,3 +248,43 @@ extension NSWindow.Level {
public static let minimum = level(for: .minimumWindow)
public static let maximum = level(for: .maximumWindow)
}


struct App {
static let url = Bundle.main.bundleURL

static func quit() {
NSApp.terminate(nil)
}

static func relaunch() {
let configuration = NSWorkspace.OpenConfiguration()
configuration.createsNewApplicationInstance = true

NSWorkspace.shared.openApplication(at: url, configuration: configuration) { _, error in
DispatchQueue.main.async {
if let error = error {
NSApp.presentError(error)
return
}

quit()
}
}
}
}


extension NSScreen {
// Returns a publisher that sends updates when anything related to screens change.
// This includes screens being added/removed, resolution change, and the screen frame changing (dock and menu bar being toggled).
static var publisher: AnyPublisher<Void, Never> {
Publishers.Merge(
NotificationCenter.default.publisher(for: NSApplication.didChangeScreenParametersNotification),
// We use a wake up notification as the screen setup might have changed during sleep. For example, a screen could have been unplugged.
NotificationCenter.default.publisher(for: NSWorkspace.didWakeNotification)
)
.map { _ in }
.eraseToAnyPublisher()
}
}
6 changes: 1 addition & 5 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ Right-clicking or option-clicking the menu bar icon displays a menu with options

<img src="screenshot.png" width="1129">


## Getting started

#### [Download the latest release](https://sindresorhus.com/touch-bar-simulator)
Expand All @@ -24,7 +23,7 @@ $ brew cask install touch-bar-simulator
```


*Requires macOS 10.14 or later.*
*Requires macOS 10.15 or later.*


## Screenshot
Expand Down Expand Up @@ -64,22 +63,19 @@ No, we're not interested in localizing the app.

Xcode 10 moved the required private symbols needed to trigger the Touch Bar simulator into the main IDEKit framework, which has a lot of dependencies on its own. I managed to get it working by including all those frameworks, but the app ended up being 700 MB... I then went back to the drawing board. I discovered a way to communicate with the Touch Bar simulator directly. The result of this is a faster and more stable app.


## Build

```
./build
```


## Related

- [Website](https://sindresorhus.com/touch-bar-simulator/)
- [Product Hunt submission](https://www.producthunt.com/posts/touch-bar-simulator)
- [Gifski](https://github.com/sindresorhus/Gifski) - Convert videos to high-quality GIFs on your Mac
- [More apps…](https://sindresorhus.com/apps)


## Maintainers

- [Sindre Sorhus](https://github.com/sindresorhus)
Expand Down

0 comments on commit 2ecf880

Please sign in to comment.