|
|
|
@@ -86,11 +86,11 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
var consoleWindow: ConsoleWindow?
|
|
|
|
|
|
|
|
|
|
// The console needs a parent view controller in order to display context menus.
|
|
|
|
|
let viewController = UIViewController()
|
|
|
|
|
lazy var viewController = UIViewController()
|
|
|
|
|
lazy var consoleView = viewController.view!
|
|
|
|
|
|
|
|
|
|
/// Text view that displays printed items.
|
|
|
|
|
let consoleTextView = InvertedTextView()
|
|
|
|
|
lazy var consoleTextView = InvertedTextView()
|
|
|
|
|
|
|
|
|
|
/// Button that reveals menu.
|
|
|
|
|
lazy var menuButton = UIButton()
|
|
|
|
@@ -99,7 +99,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
var scrollLocked = true
|
|
|
|
|
|
|
|
|
|
/// Feedback generator for the long press action.
|
|
|
|
|
let feedbackGenerator = UISelectionFeedbackGenerator()
|
|
|
|
|
lazy var feedbackGenerator = UISelectionFeedbackGenerator()
|
|
|
|
|
|
|
|
|
|
lazy var panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(consolePiPPanner(recognizer:)))
|
|
|
|
|
lazy var longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressAction(recognizer:)))
|
|
|
|
@@ -303,8 +303,6 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private var _hasRelayedOffsetChange = false
|
|
|
|
|
|
|
|
|
|
/// Print items to the console view.
|
|
|
|
|
public func print(_ items: Any) {
|
|
|
|
|
if currentText == "" {
|
|
|
|
@@ -382,12 +380,46 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var dynamicReportTimer: Timer? {
|
|
|
|
|
willSet { dynamicReportTimer?.invalidate() }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func systemReport() {
|
|
|
|
|
DispatchQueue.main.async { [self] in
|
|
|
|
|
|
|
|
|
|
if currentText != "" { print("\n") }
|
|
|
|
|
|
|
|
|
|
dynamicReportTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
|
|
|
|
|
var _currentText = currentText
|
|
|
|
|
|
|
|
|
|
// To optimize performance, only scan the last 2500 characters of text for system report changes.
|
|
|
|
|
let range: NSRange = {
|
|
|
|
|
if _currentText.count <= 2500 {
|
|
|
|
|
return NSMakeRange(0, _currentText.count)
|
|
|
|
|
}
|
|
|
|
|
return NSMakeRange(_currentText.count - 2500, 2500)
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
let regex0 = try! NSRegularExpression(pattern: "Thermal State: .*", options: NSRegularExpression.Options.caseInsensitive)
|
|
|
|
|
_currentText = regex0.stringByReplacingMatches(in: _currentText, options: [], range: range, withTemplate: "Thermal State: \(SystemReport.shared.thermalState)")
|
|
|
|
|
|
|
|
|
|
let regex1 = try! NSRegularExpression(pattern: "System Uptime: .*", options: NSRegularExpression.Options.caseInsensitive)
|
|
|
|
|
_currentText = regex1.stringByReplacingMatches(in: _currentText, options: [], range: range, withTemplate: "System Uptime: \(ProcessInfo.processInfo.systemUptime.formattedString!)")
|
|
|
|
|
|
|
|
|
|
let regex2 = try! NSRegularExpression(pattern: "Low Power Mode: .*", options: NSRegularExpression.Options.caseInsensitive)
|
|
|
|
|
_currentText = regex2.stringByReplacingMatches(in: _currentText, options: [], range: range, withTemplate: "Low Power Mode: \(ProcessInfo.processInfo.isLowPowerModeEnabled)")
|
|
|
|
|
|
|
|
|
|
if currentText != _currentText {
|
|
|
|
|
currentText = _currentText
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
// Invalidate the timer if there is no longer anything to update.
|
|
|
|
|
timer.invalidate()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
print(
|
|
|
|
|
"""
|
|
|
|
|
\n
|
|
|
|
|
Model Name: \(SystemReport.shared.gestaltMarketingName)
|
|
|
|
|
Model Identifier: \(SystemReport.shared.gestaltModelIdentifier)
|
|
|
|
|
Architecture: \(SystemReport.shared.gestaltArchitecture)
|
|
|
|
@@ -398,20 +430,20 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
Memory: \(round(100 * Double(ProcessInfo.processInfo.physicalMemory) * pow(10, -9)) / 100) GB
|
|
|
|
|
Processor Cores: \(Int(ProcessInfo.processInfo.processorCount))
|
|
|
|
|
Thermal State: \(SystemReport.shared.thermalState)
|
|
|
|
|
System Uptime: \(Int(ProcessInfo.processInfo.systemUptime))s
|
|
|
|
|
System Uptime: \(ProcessInfo.processInfo.systemUptime.formattedString!)
|
|
|
|
|
Low Power Mode: \(ProcessInfo.processInfo.isLowPowerModeEnabled)
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func displayReport() {
|
|
|
|
|
DispatchQueue.main.async { [self] in
|
|
|
|
|
|
|
|
|
|
if currentText != "" { print("\n") }
|
|
|
|
|
|
|
|
|
|
print(
|
|
|
|
|
"""
|
|
|
|
|
\n
|
|
|
|
|
Screen Size: \(UIScreen.main.bounds.size)
|
|
|
|
|
Screen Corner Radius: \(UIScreen.main.value(forKey: "_displ" + "ayCorn" + "erRa" + "dius") as! CGFloat)
|
|
|
|
|
Screen Scale: \(UIScreen.main.scale)
|
|
|
|
@@ -428,15 +460,15 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
|
|
|
|
|
func commitTextChanges(requestMenuUpdate menuUpdateRequested: Bool) {
|
|
|
|
|
|
|
|
|
|
if consoleTextView.contentOffset.y > consoleTextView.contentSize.height - 20 - consoleTextView.bounds.size.height ||
|
|
|
|
|
_hasRelayedOffsetChange == false {
|
|
|
|
|
consoleTextView.pendingOffsetChange = true
|
|
|
|
|
if consoleTextView.contentOffset.y > consoleTextView.contentSize.height - consoleTextView.bounds.size.height - 20 {
|
|
|
|
|
|
|
|
|
|
_hasRelayedOffsetChange = true
|
|
|
|
|
// Weird, weird fix that makes the scroll view bottom pinning system work.
|
|
|
|
|
consoleTextView.isScrollEnabled.toggle()
|
|
|
|
|
consoleTextView.isScrollEnabled.toggle()
|
|
|
|
|
|
|
|
|
|
consoleTextView.pendingOffsetChange = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
consoleTextView.text = currentText
|
|
|
|
|
|
|
|
|
|
setAttributedText(currentText)
|
|
|
|
|
|
|
|
|
|
if menuUpdateRequested {
|
|
|
|
@@ -772,3 +804,15 @@ class InvertedTextView: UITextView {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extension TimeInterval {
|
|
|
|
|
var formattedString: String? {
|
|
|
|
|
let formatter = DateComponentsFormatter()
|
|
|
|
|
formatter.allowedUnits = [.hour, .minute, .second]
|
|
|
|
|
return formatter.string(from: self)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fileprivate func _debugPrint(_ items: Any) {
|
|
|
|
|
print(items)
|
|
|
|
|
}
|
|
|
|
|