1
0
mirror of https://github.com/Toxblh/MTMR.git synced 2026-01-10 17:08:39 +00:00

Merge pull request #20 from ad/apps

Launched apps (like dock)
This commit is contained in:
Anton Palgunov 2018-04-16 23:15:47 +01:00 committed by GitHub
commit 71ea94590b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 212 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View File

@ -16,6 +16,8 @@
36C2ECE0207CB1B0003CDA33 /* defaultPreset.json in Resources */ = {isa = PBXBuildFile; fileRef = 36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */; };
6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */; };
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */; };
6042B6A72083E03A00C525C8 /* AppScrubberTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */; };
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */ = {isa = PBXBuildFile; fileRef = 6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */; };
B0008E552080286C003AD4DD /* SupportHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0008E542080286C003AD4DD /* SupportHelpers.swift */; };
B05600D32083E9BB00EB218D /* CustomSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B05600D22083E9BB00EB218D /* CustomSlider.swift */; };
B059D622205E03F5006E6B86 /* TouchBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D621205E03F5006E6B86 /* TouchBarController.swift */; };
@ -61,6 +63,9 @@
36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = defaultPreset.json; sourceTree = "<group>"; };
6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrightnessViewController.swift; sourceTree = "<group>"; };
6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VolumeViewController.swift; sourceTree = "<group>"; };
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppScrubberTouchBarItem.swift; sourceTree = "<group>"; };
6042B6A82083E1F500C525C8 /* DeprecatedCarbonAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeprecatedCarbonAPI.h; sourceTree = "<group>"; };
6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = DeprecatedCarbonAPI.c; sourceTree = "<group>"; };
B0008E542080286C003AD4DD /* SupportHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportHelpers.swift; sourceTree = "<group>"; };
B05600D22083E9BB00EB218D /* CustomSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSlider.swift; sourceTree = "<group>"; };
B059D621205E03F5006E6B86 /* TouchBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchBarController.swift; sourceTree = "<group>"; };
@ -165,6 +170,7 @@
6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */,
6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */,
368EDDE620812A1D00E10953 /* ScrollViewItem.swift */,
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */,
B05600D22083E9BB00EB218D /* CustomSlider.swift */,
);
path = MTMR;
@ -203,6 +209,8 @@
B059D62A205F0E7D006E6B86 /* TouchBarPrivateApi-Bridging.h */,
B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */,
B0F8771B207AC92700D6E430 /* TouchBarSupport.h */,
6042B6A82083E1F500C525C8 /* DeprecatedCarbonAPI.h */,
6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */,
);
path = CBridge;
sourceTree = "<group>";
@ -328,9 +336,11 @@
36C2ECD9207B74B4003CDA33 /* AppleScriptTouchBarItem.swift in Sources */,
B0F8771A207AC1EA00D6E430 /* TouchBarSupport.m in Sources */,
B0008E552080286C003AD4DD /* SupportHelpers.swift in Sources */,
6042B6A72083E03A00C525C8 /* AppScrubberTouchBarItem.swift in Sources */,
B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */,
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */,
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */,
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */,
B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */,
36C2ECDB207C3FE7003CDA33 /* ItemsParsing.swift in Sources */,
B0A7E9AA205D6AA400EEF070 /* KeyPress.swift in Sources */,

View File

@ -0,0 +1,118 @@
//
// AppScrubberTouchBarItem.swift
//
// This file is part of TouchDock
// Copyright (C) 2017 Xander Deng
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
import Cocoa
class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrubberDataSource {
var scrubber: NSScrubber!
var runningApplications: [NSRunningApplication] = []
override init(identifier: NSTouchBarItem.Identifier) {
super.init(identifier: identifier)
scrubber = NSScrubber();
scrubber.delegate = self
scrubber.dataSource = self
scrubber.mode = .free // .fixed
let layout = NSScrubberFlowLayout();
layout.itemSize = NSSize(width: 44, height: 30)
scrubber.scrubberLayout = layout
scrubber.selectionBackgroundStyle = .roundedBackground
view = scrubber
scrubber.register(NSScrubberImageItemView.self, forItemIdentifier: NSUserInterfaceItemIdentifier(rawValue: "ScrubberApplicationsItemReuseIdentifier"))
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(activeApplicationChanged), name: NSWorkspace.didLaunchApplicationNotification, object: nil)
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(activeApplicationChanged), name: NSWorkspace.didTerminateApplicationNotification, object: nil)
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(activeApplicationChanged), name: NSWorkspace.didActivateApplicationNotification, object: nil)
updateRunningApplication()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func activeApplicationChanged(n: Notification) {
updateRunningApplication()
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
updateRunningApplication()
}
func updateRunningApplication() {
let newApplications = launchedApplications().filter {
!$0.isTerminated && $0.bundleIdentifier != nil
}
let frontmost = NSWorkspace.shared.frontmostApplication
let index = newApplications.index {
$0.processIdentifier == frontmost?.processIdentifier
}
runningApplications = newApplications
scrubber.reloadData()
scrubber.selectedIndex = index ?? 0
}
public func numberOfItems(for scrubber: NSScrubber) -> Int {
return runningApplications.count
}
public func scrubber(_ scrubber: NSScrubber, viewForItemAt index: Int) -> NSScrubberItemView {
let item = scrubber.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "ScrubberApplicationsItemReuseIdentifier"), owner: self) as? NSScrubberImageItemView ?? NSScrubberImageItemView()
item.imageView.imageScaling = .scaleProportionallyDown
if let icon = runningApplications[index].icon {
item.image = icon
}
return item
}
public func didFinishInteracting(with scrubber: NSScrubber) {
runningApplications[scrubber.selectedIndex].activate(options: [ .activateIgnoringOtherApps ])
// NB: if you can't open app which on another space, try to check mark
// "When switching to an application, switch to a Space with open windows for the application"
// in Mission control settings
// TODO: deminiaturize app
// if let info = CGWindowListCopyWindowInfo(.optionOnScreenOnly, kCGNullWindowID) as? [[ String : Any]] {
// for dict in info {
// if dict["kCGWindowOwnerName"] as! String == runningApplications[scrubber.selectedIndex].localizedName {
// print(dict["kCGWindowNumber"], dict["kCGWindowOwnerName"])
// }
// }
// }
}
}
private func launchedApplications() -> [NSRunningApplication] {
let asns = _LSCopyApplicationArrayInFrontToBackOrder(~0)?.takeRetainedValue()
return (0..<CFArrayGetCount(asns)).compactMap { index in
let asn = CFArrayGetValueAtIndex(asns, index)
let pid = pidFromASN(asn)
return NSRunningApplication(processIdentifier: pid)
}
}

View File

@ -0,0 +1,45 @@
//
// DeprecatedCarbonAPI.c
//
// This file is part of TouchDock
// Copyright (C) 2017 Xander Deng
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#include "DeprecatedCarbonAPI.h"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
CFStringRef kPidKey = CFSTR("pid");
pid_t pidFromASN(void const *asn) {
pid_t pid = -1;
ProcessSerialNumber psn = {kNoProcess, kNoProcess};
if (CFGetTypeID(asn) == _LSASNGetTypeID()) {
_LSASNExtractHighAndLowParts(asn, &psn.highLongOfPSN, &psn.lowLongOfPSN);
CFDictionaryRef processInfo = ProcessInformationCopyDictionary(&psn, kProcessDictionaryIncludeAllInformationMask);
if (processInfo) {
CFNumberRef pidNumber = CFDictionaryGetValue(processInfo, kPidKey);
if (pidNumber) {
CFNumberGetValue(pidNumber, kCFNumberSInt32Type, &pid);
}
CFRelease(processInfo);
}
}
return pid;
}
#pragma GCC diagnostic pop

View File

@ -0,0 +1,27 @@
//
// DeprecatedCarbonAPI.h
//
// This file is part of TouchDock
// Copyright (C) 2017 Xander Deng
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#import <Carbon/Carbon.h>
extern CFArrayRef _LSCopyApplicationArrayInFrontToBackOrder(uint32_t sessionID);
extern void _LSASNExtractHighAndLowParts(void const* asn, UInt32* psnHigh, UInt32* psnLow);
extern CFTypeID _LSASNGetTypeID(void);
pid_t pidFromASN(void const *asn);

View File

@ -8,6 +8,7 @@
#import "TouchBarPrivateApi.h"
#import "TouchBarSupport.h"
#import "DeprecatedCarbonAPI.h"
NS_ASSUME_NONNULL_BEGIN

View File

@ -80,6 +80,9 @@ class SupportedTypesHolder {
let item = ItemType.appleScriptTitledButton(source: Source(filePath: scriptPath), refreshInterval: interval ?? 1800.0)
return (item: item, action: .none, parameters: [:])
},
"dock": { decoder in
return (item: .dock(), action: .none, parameters: [:])
},
"volume": { decoder in
enum CodingKeys: String, CodingKey { case image }
let container = try decoder.container(keyedBy: CodingKeys.self)
@ -134,6 +137,7 @@ enum ItemType: Decodable {
case staticButton(title: String)
case appleScriptTitledButton(source: SourceProtocol, refreshInterval: Double)
case timeButton(formatTemplate: String)
case dock()
case volume()
case brightness(refreshInterval: Double)
@ -150,6 +154,7 @@ enum ItemType: Decodable {
case staticButton
case appleScriptTitledButton
case timeButton
case dock
case volume
case brightness
}
@ -168,6 +173,8 @@ enum ItemType: Decodable {
case .timeButton:
let template = try container.decodeIfPresent(String.self, forKey: .formatTemplate) ?? "HH:mm"
self = .timeButton(formatTemplate: template)
case .dock:
self = .dock()
case .volume:
self = .volume()
case .brightness:

View File

@ -23,6 +23,8 @@ extension ItemType {
return "com.toxblh.mtmr.appleScriptButton."
case .timeButton(formatTemplate: _):
return "com.toxblh.mtmr.timeButton."
case .dock():
return "com.toxblh.mtmr.dock"
case .volume():
return "com.toxblh.mtmr.volume"
case .brightness(refreshInterval: _):
@ -150,6 +152,8 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval, onTap: action)
case .timeButton(formatTemplate: let template):
barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template)
case .dock:
barItem = AppScrubberTouchBarItem(identifier: identifier)
case .volume:
if case .image(let source)? = item.additionalParameters[.image] {
barItem = VolumeViewController(identifier: identifier, image: source.image)