mirror of
https://github.com/utmapp/UTM.git
synced 2026-10-12 02:24:01 +00:00
Merge tag 'v2.4.1' into dev-monterey
This commit is contained in:
+2
-2
@@ -17,8 +17,8 @@
|
||||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
MARKETING_VERSION = 2.4.0
|
||||
CURRENT_PROJECT_VERSION = 39
|
||||
MARKETING_VERSION = 2.4.1
|
||||
CURRENT_PROJECT_VERSION = 40
|
||||
|
||||
// Codesigning settings defined optionally, see Documentation/iOSDevelopment.md
|
||||
#include? "CodeSigning.xcconfig"
|
||||
|
||||
@@ -720,6 +720,10 @@ static void cs_channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer
|
||||
return point;
|
||||
}
|
||||
|
||||
- (BOOL)cursorInverted {
|
||||
return !self.isGLEnabled;
|
||||
}
|
||||
|
||||
- (void)forceCursorPosition:(CGPoint)pos {
|
||||
self.mouseGuest = pos;
|
||||
}
|
||||
|
||||
@@ -78,9 +78,9 @@
|
||||
|
||||
- (void)loadSoundDefaultsForTarget:(nullable NSString *)target architecture:(nullable NSString *)architecture {
|
||||
NSString *card = nil;
|
||||
if ([target hasPrefix:@"pc"] || [target hasPrefix:@"q35"]) {
|
||||
if ([target hasPrefix:@"pc"]) {
|
||||
card = @"AC97";
|
||||
} else if ([target isEqualToString:@"virt"] || [target hasPrefix:@"virt-"]) {
|
||||
} else if ([target hasPrefix:@"q35"] || [target isEqualToString:@"virt"] || [target hasPrefix:@"virt-"]) {
|
||||
card = @"intel-hda";
|
||||
} else if ([target isEqualToString:@"mac99"]) {
|
||||
card = @"screamer";
|
||||
@@ -97,13 +97,15 @@
|
||||
return;
|
||||
}
|
||||
self.soundCard = card;
|
||||
self.soundEnabled = NO;
|
||||
self.soundEnabled = YES;
|
||||
}
|
||||
|
||||
- (void)loadNetworkDefaultsForTarget:(nullable NSString *)target architecture:(nullable NSString *)architecture {
|
||||
NSString *card = nil;
|
||||
if ([target hasPrefix:@"pc"] || [target hasPrefix:@"q35"]) {
|
||||
if ([target hasPrefix:@"pc"]) {
|
||||
card = @"rtl8139";
|
||||
} else if ([target hasPrefix:@"q35"]) {
|
||||
card = @"e1000";
|
||||
} else if ([target isEqualToString:@"virt"] || [target hasPrefix:@"virt-"]) {
|
||||
card = @"virtio-net-pci";
|
||||
} else if (architecture) {
|
||||
@@ -188,8 +190,6 @@
|
||||
return @"cortex-a72";
|
||||
} else if ([architecture isEqualToString:@"arm"]) {
|
||||
return @"cortex-a15";
|
||||
} else if ([target hasPrefix:@"pc"] || [target hasPrefix:@"q35"]) {
|
||||
return @"Skylake-Client";
|
||||
} else {
|
||||
return @"default";
|
||||
}
|
||||
|
||||
+20
-14
@@ -109,17 +109,19 @@ static void utm_migration_pass_handler(int64_t pass, void *ctx) {
|
||||
|
||||
}
|
||||
|
||||
typedef void(^rpcCompletionHandler_t)(NSDictionary *, NSError *);
|
||||
|
||||
@interface UTMQemuManager ()
|
||||
|
||||
@property (nonatomic, readwrite) BOOL isConnected;
|
||||
@property (nonatomic, readwrite) NSInteger retries;
|
||||
@property (nonatomic, nullable) qemuManagerCompletionHandler_t completionCallback;
|
||||
@property (nonatomic, nullable) rpcCompletionHandler_t rpcCallback;
|
||||
|
||||
@end
|
||||
|
||||
@implementation UTMQemuManager {
|
||||
UTMJSONStream *_jsonStream;
|
||||
void (^_rpc_finish)(NSDictionary *, NSError *);
|
||||
dispatch_semaphore_t _cmd_lock;
|
||||
}
|
||||
|
||||
@@ -128,20 +130,24 @@ void qmp_rpc_call(CFDictionaryRef args, CFDictionaryRef *ret, Error **err, void
|
||||
dispatch_semaphore_t rpc_sema = dispatch_semaphore_create(0);
|
||||
__block NSDictionary *dict;
|
||||
__block NSError *nserr;
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_semaphore_wait(self->_cmd_lock, DISPATCH_TIME_FOREVER);
|
||||
self->_rpc_finish = ^(NSDictionary *ret_dict, NSError *ret_err){
|
||||
self.rpcCallback = ^(NSDictionary *ret_dict, NSError *ret_err){
|
||||
dispatch_semaphore_t rpc_sema_copy = rpc_sema;
|
||||
NSCAssert(ret_dict || ret_err, @"Both dict and err are null");
|
||||
nserr = ret_err;
|
||||
dict = ret_dict;
|
||||
self->_rpc_finish = nil;
|
||||
_self.rpcCallback = nil;
|
||||
dispatch_semaphore_signal(rpc_sema_copy); // copy to avoid race condition
|
||||
};
|
||||
if (![self.jsonStream sendDictionary:(__bridge NSDictionary *)args]) {
|
||||
self->_rpc_finish(nil, [NSError errorWithDomain:kUTMErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"No connection for RPC.", "UTMQemuManager")}]);
|
||||
if (![self.jsonStream sendDictionary:(__bridge NSDictionary *)args] && self.rpcCallback) {
|
||||
self.rpcCallback(nil, [NSError errorWithDomain:kUTMErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"No connection for RPC.", "UTMQemuManager")}]);
|
||||
}
|
||||
if (dispatch_semaphore_wait(rpc_sema, dispatch_time(DISPATCH_TIME_NOW, kRPCTimeout)) != 0) {
|
||||
self->_rpc_finish = nil;
|
||||
// possible race between this timeout and the callback being triggered
|
||||
self.rpcCallback = ^(NSDictionary *ret_dict, NSError *ret_err){
|
||||
_self.rpcCallback = nil;
|
||||
};
|
||||
nserr = [NSError errorWithDomain:kUTMErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"Timed out waiting for RPC.", "UTMQemuManager")}];
|
||||
}
|
||||
if (ret) {
|
||||
@@ -168,8 +174,8 @@ void qmp_rpc_call(CFDictionaryRef args, CFDictionaryRef *ret, Error **err, void
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (_rpc_finish) {
|
||||
_rpc_finish(nil, [NSError errorWithDomain:kUTMErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"Manager being deallocated, killing pending RPC.", "UTMQemuManager")}]);
|
||||
if (self.rpcCallback) {
|
||||
self.rpcCallback(nil, [NSError errorWithDomain:kUTMErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"Manager being deallocated, killing pending RPC.", "UTMQemuManager")}]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,8 +201,8 @@ void qmp_rpc_call(CFDictionaryRef args, CFDictionaryRef *ret, Error **err, void
|
||||
|
||||
- (void)jsonStream:(UTMJSONStream *)stream seenError:(NSError *)error {
|
||||
UTMLog(@"QMP stream error seen: %@", error);
|
||||
if (_rpc_finish) {
|
||||
_rpc_finish(nil, error);
|
||||
if (self.rpcCallback) {
|
||||
self.rpcCallback(nil, error);
|
||||
}
|
||||
[self disconnect];
|
||||
if (!self.isConnected && self.retries > 0) {
|
||||
@@ -214,15 +220,15 @@ void qmp_rpc_call(CFDictionaryRef args, CFDictionaryRef *ret, Error **err, void
|
||||
- (void)jsonStream:(UTMJSONStream *)stream receivedDictionary:(NSDictionary *)dict {
|
||||
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id val, BOOL *stop) {
|
||||
if ([key isEqualToString:@"return"]) {
|
||||
if (self->_rpc_finish) {
|
||||
self->_rpc_finish(dict, nil);
|
||||
if (self.rpcCallback) {
|
||||
self.rpcCallback(dict, nil);
|
||||
} else {
|
||||
UTMLog(@"Got unexpected 'return' response: %@", dict);
|
||||
}
|
||||
*stop = YES;
|
||||
} else if ([key isEqualToString:@"error"]) {
|
||||
if (self->_rpc_finish) {
|
||||
self->_rpc_finish(nil, [NSError errorWithDomain:kUTMErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: dict[@"error"][@"desc"]}]);
|
||||
if (self.rpcCallback) {
|
||||
self.rpcCallback(nil, [NSError errorWithDomain:kUTMErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: dict[@"error"][@"desc"]}]);
|
||||
} else {
|
||||
UTMLog(@"Got unexpected 'error' response: %@", dict);
|
||||
}
|
||||
|
||||
@@ -235,9 +235,11 @@ static size_t sysctl_read(const char *name) {
|
||||
|
||||
- (void)argsForCpu {
|
||||
if ([self.configuration.systemCPU isEqualToString:@"default"] && self.useHypervisor) {
|
||||
// if default and not hypervisor, we don't pass any -cpu argument
|
||||
// if default and not hypervisor, we don't pass any -cpu argument for x86 and use host for ARM
|
||||
#if !defined(__x86_64__)
|
||||
[self pushArgv:@"-cpu"];
|
||||
[self pushArgv:@"host"];
|
||||
#endif
|
||||
} else if (self.configuration.systemCPU.length > 0 && ![self.configuration.systemCPU isEqualToString:@"default"]) {
|
||||
NSString *cpu = self.configuration.systemCPU;
|
||||
for (NSString *flag in self.configuration.systemCPUFlags) {
|
||||
@@ -526,7 +528,8 @@ static size_t sysctl_read(const char *name) {
|
||||
|
||||
- (BOOL)useOnlyPcores {
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
return [defaults boolForKey:@"UseOnlyPcores"];
|
||||
BOOL isUnset = ![defaults objectForKey:@"UseOnlyPcores"];
|
||||
return isUnset || [defaults boolForKey:@"UseOnlyPcores"];
|
||||
}
|
||||
|
||||
- (BOOL)hasCustomBios {
|
||||
|
||||
@@ -95,7 +95,11 @@ struct ContentView: View {
|
||||
#if !WITH_QEMU_TCI
|
||||
if !Main.jitAvailable {
|
||||
data.busyWork {
|
||||
#if canImport(AltKit)
|
||||
try data.startAltJIT()
|
||||
#else
|
||||
throw NSLocalizedString("Your version of iOS does not support running VMs while unmodified. You must either run UTM while jailbroken or with a remote debugger attached.", comment: "ContentView")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import Foundation
|
||||
import Logging
|
||||
import Zip
|
||||
import ZIPFoundation
|
||||
|
||||
/// Downloads a ZIPped UTM file from the web, unzips it and imports it as a UTM virtual machine.
|
||||
@available(iOS 14, macOS 11, *)
|
||||
@@ -41,20 +41,31 @@ class UTMImportFromWebTask: NSObject, URLSessionDelegate, URLSessionDownloadDele
|
||||
let tempDir = fileManager.temporaryDirectory
|
||||
let originalFilename = downloadTask.originalRequest!.url!.lastPathComponent
|
||||
let downloadedZip = tempDir.appendingPathComponent(originalFilename)
|
||||
var fileURL: URL? = nil
|
||||
do {
|
||||
if fileManager.fileExists(atPath: downloadedZip.absoluteString) {
|
||||
try fileManager.removeItem(at: downloadedZip)
|
||||
}
|
||||
try fileManager.moveItem(at: location, to: downloadedZip)
|
||||
let unzippedURL = try Zip.quickUnzipFile(downloadedZip)
|
||||
let utmURL = try partialUnzipOnlyUtmVM(zipFileURL: downloadedZip, destinationFolder: data.documentsURL, fileManager: fileManager)
|
||||
/// set the url so we know, if it fails after this step the UTM in the ZIP is corrupted
|
||||
fileURL = utmURL
|
||||
/// remove the downloaded ZIP file
|
||||
try fileManager.removeItem(at: downloadedZip)
|
||||
handleUnzipped(unzippedURL)
|
||||
/// remove unzipped file
|
||||
try FileManager.default.removeItem(at: unzippedURL)
|
||||
/// load the downloaded VM into the UI
|
||||
try self.data.readUTMFromURL(fileURL: utmURL)
|
||||
} catch {
|
||||
logger.error(Logger.Message(stringLiteral: error.localizedDescription))
|
||||
try? fileManager.removeItem(at: downloadedZip)
|
||||
DispatchQueue.main.async {
|
||||
self.data.alertMessage = AlertMessage(error.localizedDescription)
|
||||
}
|
||||
if let fileURL = fileURL {
|
||||
/// remove imported UTM, as it is corrupted
|
||||
try? fileManager.removeItem(at: fileURL)
|
||||
} else {
|
||||
/// failed earlier
|
||||
try? fileManager.removeItem(at: downloadedZip)
|
||||
}
|
||||
}
|
||||
/// remove downloading VM View Model
|
||||
DispatchQueue.main.async { [self] in
|
||||
@@ -64,8 +75,52 @@ class UTMImportFromWebTask: NSObject, URLSessionDelegate, URLSessionDownloadDele
|
||||
}
|
||||
}
|
||||
|
||||
private func partialUnzipOnlyUtmVM(zipFileURL: URL, destinationFolder: URL, fileManager: FileManager) throws -> URL {
|
||||
let utmFileEnding = ".utm"
|
||||
let utmDirectoryEnding = "\(utmFileEnding)/"
|
||||
if let archive = Archive(url: zipFileURL, accessMode: .read),
|
||||
/// find the UTM directory and its contents
|
||||
let utmFolderInZip = archive.first(where: { $0.path.hasSuffix(utmDirectoryEnding) }) {
|
||||
/// get the UTM package filename
|
||||
let originalFileName = URL(fileURLWithPath: utmFolderInZip.path).lastPathComponent
|
||||
var destinationUtmDirectory = originalFileName
|
||||
/// check if the UTM already exists
|
||||
var duplicateIndex = 1
|
||||
var exists = false
|
||||
repeat {
|
||||
exists = data.virtualMachines.contains(where: {
|
||||
return $0.path != nil && $0.path!.lastPathComponent == destinationUtmDirectory
|
||||
})
|
||||
if exists {
|
||||
destinationUtmDirectory = originalFileName.replacingOccurrences(of: utmFileEnding, with: " (\(duplicateIndex))\(utmFileEnding)")
|
||||
duplicateIndex += 1
|
||||
}
|
||||
} while exists
|
||||
/// got destination folder name
|
||||
let destinationURL = destinationFolder.appendingPathComponent(destinationUtmDirectory, isDirectory: true)
|
||||
/// create the .utm directory
|
||||
try fileManager.createDirectory(at: destinationURL, withIntermediateDirectories: false)
|
||||
/// get and extract all files contained in the UTM directory, except the `__MACOSX` folder
|
||||
let containedFiles = archive.filter({ $0.path.contains(utmDirectoryEnding) && !$0.path.hasSuffix(utmDirectoryEnding) && !$0.path.contains("__MACOSX") })
|
||||
for file in containedFiles {
|
||||
let relativePath = file.path.replacingOccurrences(of: utmFolderInZip.path, with: "")
|
||||
let isDirectory = file.path.hasSuffix("/")
|
||||
_ = try archive.extract(file, to: destinationURL.appendingPathComponent(relativePath, isDirectory: isDirectory))
|
||||
}
|
||||
return destinationURL
|
||||
} else {
|
||||
throw UnzipNoUTMFileError()
|
||||
}
|
||||
}
|
||||
|
||||
private class UnzipNoUTMFileError: Error {
|
||||
var localizedDescription: String {
|
||||
NSLocalizedString("There is no UTM file in the downloaded ZIP archive.", comment: "Error shown when importing a ZIP file from web that doesn't contain a UTM Virtual Machine.")
|
||||
}
|
||||
}
|
||||
|
||||
/// received when the download progresses
|
||||
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
|
||||
internal func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
|
||||
DispatchQueue.main.async { [self] in
|
||||
guard pendingVM != nil else { return }
|
||||
let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
|
||||
@@ -108,24 +163,6 @@ class UTMImportFromWebTask: NSObject, URLSessionDelegate, URLSessionDownloadDele
|
||||
}
|
||||
}
|
||||
|
||||
/// Call on background queue
|
||||
private func handleUnzipped(_ unzippedFolder: URL) {
|
||||
do {
|
||||
let path = unzippedFolder.path
|
||||
/// try to find .utm file in unzipped folder
|
||||
if let utmFilename = try FileManager.default.contentsOfDirectory(atPath: path).first(where: { $0.hasSuffix(".utm") }) {
|
||||
/// got filename
|
||||
let utmURL = URL(fileURLWithPath: path).appendingPathComponent(utmFilename, isDirectory: false)
|
||||
try self.data.importUTM(url: utmURL)
|
||||
} else {
|
||||
/// utm file not in folder
|
||||
logger.error("No UTM file in extracted ZIP")
|
||||
}
|
||||
} catch {
|
||||
logger.error(Logger.Message(stringLiteral: error.localizedDescription))
|
||||
}
|
||||
}
|
||||
|
||||
/// Downloads a ZIP-compressed file from the provided URL and imports the UTM file inside, if there is one.
|
||||
func startDownload() -> UTMPendingVirtualMachine {
|
||||
/// begin the download
|
||||
@@ -146,7 +183,7 @@ class UTMImportFromWebTask: NSObject, URLSessionDelegate, URLSessionDownloadDele
|
||||
/// Cancels the network request, if any.
|
||||
/// Cancelling the file operations that occur after the download has finished is not supported.
|
||||
func cancel() {
|
||||
guard !isDone && !downloadTask.progress.isFinished else { return }
|
||||
guard !isDone && downloadTask != nil && !downloadTask.progress.isFinished else { return }
|
||||
downloadTask.cancel()
|
||||
downloadTask = nil
|
||||
}
|
||||
|
||||
+60
-5
@@ -20,6 +20,9 @@ import AppKit
|
||||
#else
|
||||
import UIKit
|
||||
#endif
|
||||
#if canImport(AltKit)
|
||||
import AltKit
|
||||
#endif
|
||||
|
||||
@available(iOS 14, macOS 11, *)
|
||||
struct AlertMessage: Identifiable {
|
||||
@@ -71,8 +74,11 @@ class UTMData: ObservableObject {
|
||||
fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
||||
}
|
||||
|
||||
private var busyQueue: DispatchQueue
|
||||
|
||||
init() {
|
||||
let defaults = UserDefaults.standard
|
||||
self.busyQueue = DispatchQueue(label: "UTM Busy Queue", qos: .userInitiated)
|
||||
self.showSettingsModal = false
|
||||
self.showNewVMSheet = false
|
||||
self.busy = false
|
||||
@@ -336,7 +342,11 @@ class UTMData: ObservableObject {
|
||||
} else {
|
||||
try fileManager.copyItem(at: at, to: to)
|
||||
}
|
||||
guard let vm = UTMVirtualMachine(url: to) else {
|
||||
}
|
||||
|
||||
/// Attempts to read from the URL and appends the VM to the list of virtual machines.
|
||||
func readUTMFromURL(fileURL: URL) throws {
|
||||
guard let vm = UTMVirtualMachine(url: fileURL) else {
|
||||
throw NSLocalizedString("Failed to parse imported VM.", comment: "UTMData")
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
@@ -371,9 +381,11 @@ class UTMData: ObservableObject {
|
||||
} else if (fileBasePath.resolvingSymlinksInPath().path == documentsURL.appendingPathComponent("Inbox", isDirectory: true).path) {
|
||||
logger.info("moving from Inbox")
|
||||
try copyUTM(at: url, to: dest, move: true)
|
||||
try readUTMFromURL(fileURL: dest)
|
||||
} else {
|
||||
logger.info("copying to Documents")
|
||||
try copyUTM(at: url, to: dest)
|
||||
try readUTMFromURL(fileURL: dest)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -566,10 +578,10 @@ class UTMData: ObservableObject {
|
||||
}
|
||||
|
||||
func busyWork(_ work: @escaping () throws -> Void) {
|
||||
DispatchQueue.main.async {
|
||||
self.busy = true
|
||||
}
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
busyQueue.async {
|
||||
DispatchQueue.main.async {
|
||||
self.busy = true
|
||||
}
|
||||
defer {
|
||||
DispatchQueue.main.async {
|
||||
self.busy = false
|
||||
@@ -668,4 +680,47 @@ class UTMData: ObservableObject {
|
||||
tryClickAtPoint(point: point, button: button)
|
||||
#endif
|
||||
}
|
||||
|
||||
// MARK: - AltKit
|
||||
|
||||
#if canImport(AltKit)
|
||||
func startAltJIT() throws {
|
||||
let event = DispatchSemaphore(value: 0)
|
||||
var connectError: Error?
|
||||
DispatchQueue.main.async {
|
||||
ServerManager.shared.autoconnect { result in
|
||||
switch result
|
||||
{
|
||||
case .failure(let error):
|
||||
logger.error("Could not auto-connect to server. \(error.localizedDescription)")
|
||||
connectError = error
|
||||
event.signal()
|
||||
case .success(let connection):
|
||||
connection.enableUnsignedCodeExecution { result in
|
||||
switch result
|
||||
{
|
||||
case .failure(let error):
|
||||
logger.error("Could not enable JIT compilation. \(error.localizedDescription)")
|
||||
connectError = error
|
||||
case .success:
|
||||
logger.debug("Successfully enabled JIT compilation!")
|
||||
}
|
||||
|
||||
connection.disconnect()
|
||||
event.signal()
|
||||
}
|
||||
}
|
||||
}
|
||||
ServerManager.shared.startDiscovering()
|
||||
}
|
||||
defer {
|
||||
ServerManager.shared.stopDiscovering()
|
||||
}
|
||||
if event.wait(timeout: .now() + 10) == .timedOut {
|
||||
throw NSLocalizedString("Cannot find AltServer for JIT enable. You cannot run VMs until JIT is enabled.", comment: "UTMData")
|
||||
} else if let error = connectError {
|
||||
throw NSLocalizedString("AltJIT error: \(error.localizedDescription)", comment: "UTMData")
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>ALTDeviceID</key>
|
||||
<string>None</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
@@ -57,6 +59,12 @@
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSBonjourServices</key>
|
||||
<array>
|
||||
<string>_altserver._tcp</string>
|
||||
</array>
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>UTM uses the local network to find and communicate with AltServer.</string>
|
||||
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||
<string>VM background access requires location services. Location data will never leave the device.</string>
|
||||
<key>NSLocationAlwaysUsageDescription</key>
|
||||
|
||||
@@ -145,3 +145,10 @@
|
||||
/* (No Comment) */
|
||||
"Up" = "上方向键";
|
||||
|
||||
"Disable screen dimming when idle" = "闲置时不自动锁定屏幕";
|
||||
|
||||
"Do not show prompt when USB device is plugged in" = "USB 设备插入时不弹出提示";
|
||||
|
||||
"Idle" = "闲置时";
|
||||
|
||||
"Devices" = "设备";
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "UTM SE";
|
||||
|
||||
/* Privacy - Location Always and When In Use Usage Description */
|
||||
"NSLocationAlwaysAndWhenInUseUsageDescription" = "虚拟机后台运行需要使用定位服务。 位置数据永远不会离开设备。";
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "UTM SE";
|
||||
|
||||
/* Privacy - Location Always and When In Use Usage Description */
|
||||
"NSLocationAlwaysAndWhenInUseUsageDescription" = "虛擬機背景存取權需要位置服務。位置資料不會傳出手機。";
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ struct SettingsView: View {
|
||||
@AppStorage("NoHypervisor") var isNoHypervisor = false
|
||||
@AppStorage("CtrlRightClick") var isCtrlRightClick = false
|
||||
@AppStorage("NoUsbPrompt") var isNoUsbPrompt = false
|
||||
@AppStorage("UseOnlyPcores") var isUseOnlyPcores = false
|
||||
@AppStorage("UseOnlyPcores") var isUseOnlyPcores = true
|
||||
@AppStorage("AlternativeCaptureKey") var isAlternativeCaptureKey = false
|
||||
|
||||
var body: some View {
|
||||
|
||||
@@ -12,4 +12,3 @@
|
||||
|
||||
/* (No Comment) */
|
||||
"UTM virtual machine" = "UTM虚拟机";
|
||||
|
||||
|
||||
@@ -543,7 +543,7 @@
|
||||
"Play" = "继续";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Port Forward" = "更多";
|
||||
"Port Forward" = "端口转发";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Protocol" = "协议";
|
||||
@@ -573,7 +573,7 @@
|
||||
"Read Only" = "只读";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Removable" = "可扩展";
|
||||
"Removable" = "可移动";
|
||||
|
||||
/* VMConfigDrivesView
|
||||
VMConfigDrivesViewController */
|
||||
@@ -788,7 +788,7 @@
|
||||
"You must terminate the running VM before you can import a new VM." = "必须先终止正在运行的虚拟机,然后才能导入新的虚拟机。";
|
||||
|
||||
/* ContentView */
|
||||
"Your version of iOS does not support running VMs while unmodified. You must either run UTM while jailbroken or with a remote debugger attached." = "您的iOS版本不支持在未越狱的情况下运行虚拟机。您必须在越狱时运行UTM,或者附加远程调试器。";
|
||||
"Your version of iOS does not support running VMs while unmodified. You must either run UTM while jailbroken or with a remote debugger attached." = "您的iOS版本不支持在未越狱的情况下运行虚拟机。您必须在越狱时运行UTM,或者附加调试器(使用 Jitterbug 或 Xcode)。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Information" = "基本信息";
|
||||
@@ -876,3 +876,6 @@
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Export QEMU Command" = "导出QEMU命令";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Random" = "随机生成";
|
||||
|
||||
@@ -49,8 +49,9 @@ UTM is also available for macOS: https://mac.getutm.app/
|
||||
|
||||
UTM is distributed under the permissive Apache 2.0 license. However, it uses several (L)GPL components. Most are dynamically linked but the gstreamer plugins are statically linked and parts of the code are taken from qemu. Please be aware of this if you intend on redistributing this application.
|
||||
|
||||
Some icons made by [Freepik](https://www.freepik.com) from [www.flaticon.com](https://www.flaticon.com/).
|
||||
|
||||
Some icons made by [Freepik](https://www.freepik.com) from [www.flaticon.com](https://www.flaticon.com/).
|
||||
UTM uses [ZIP Foundation](https://github.com/weichsel/ZIPFoundation), which is released under the MIT License.
|
||||
|
||||
[1]: https://github.com/utmapp/UTM/actions?query=event%3Arelease+workflow%3ABuild
|
||||
[2]: screen.png
|
||||
[3]: https://github.com/ktemkin/qemu/blob/with_tcti/tcg/aarch64-tcti/README.md
|
||||
|
||||
@@ -32,6 +32,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (nonatomic, readonly) NSUInteger cursorNumVertices;
|
||||
@property (nonatomic, nullable, readonly) id<MTLBuffer> displayVertices;
|
||||
@property (nonatomic, nullable, readonly) id<MTLBuffer> cursorVertices;
|
||||
@property (nonatomic, readonly) BOOL cursorInverted;
|
||||
|
||||
- (void)rendererFrameHasRendered;
|
||||
|
||||
|
||||
@@ -171,6 +171,7 @@ static matrix_float4x4 matrix_scale_translate(CGFloat scale, CGPoint translate)
|
||||
// Render the screen first
|
||||
|
||||
bool hasAlpha = NO;
|
||||
bool isInverted = NO;
|
||||
matrix_float4x4 transform = matrix_scale_translate(source.viewportScale,
|
||||
source.viewportOrigin);
|
||||
|
||||
@@ -200,6 +201,10 @@ static matrix_float4x4 matrix_scale_translate(CGFloat scale, CGPoint translate)
|
||||
|
||||
[renderEncoder setFragmentSamplerState:_sampler
|
||||
atIndex:UTMSamplerIndexTexture];
|
||||
|
||||
[renderEncoder setFragmentBytes:&isInverted
|
||||
length:sizeof(isInverted)
|
||||
atIndex:UTMFragmentBufferIndexIsInverted];
|
||||
|
||||
// Draw the vertices of our triangles
|
||||
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle
|
||||
@@ -210,6 +215,7 @@ static matrix_float4x4 matrix_scale_translate(CGFloat scale, CGPoint translate)
|
||||
if (source.cursorVisible) {
|
||||
// Next render the cursor
|
||||
bool hasAlpha = YES;
|
||||
bool isInverted = source.cursorInverted;
|
||||
matrix_float4x4 transform = matrix_scale_translate(source.viewportScale,
|
||||
CGPointMake(source.viewportOrigin.x +
|
||||
source.cursorOrigin.x,
|
||||
@@ -231,6 +237,9 @@ static matrix_float4x4 matrix_scale_translate(CGFloat scale, CGPoint translate)
|
||||
atIndex:UTMTextureIndexBaseColor];
|
||||
[renderEncoder setFragmentSamplerState:_sampler
|
||||
atIndex:UTMSamplerIndexTexture];
|
||||
[renderEncoder setFragmentBytes:&isInverted
|
||||
length:sizeof(isInverted)
|
||||
atIndex:UTMFragmentBufferIndexIsInverted];
|
||||
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle
|
||||
vertexStart:0
|
||||
vertexCount:source.cursorNumVertices];
|
||||
|
||||
@@ -32,6 +32,11 @@ typedef enum UTMSamplerIndex
|
||||
UTMSamplerIndexTexture = 0,
|
||||
} UTMSamplerIndex;
|
||||
|
||||
typedef enum UTMFragmentBufferIndex
|
||||
{
|
||||
UTMFragmentBufferIndexIsInverted = 0,
|
||||
} UTMFragmentBufferIndex;
|
||||
|
||||
// This structure defines the layout of each vertex in the array of vertices set as an input to our
|
||||
// Metal vertex shader. Since this header is shared between our .metal shader and C code,
|
||||
// we can be sure that the layout of the vertex array in the code matches the layout that
|
||||
|
||||
@@ -85,7 +85,8 @@ vertexShader(uint vertexID [[ vertex_id ]],
|
||||
fragment float4
|
||||
samplingShader(RasterizerData in [[stage_in]],
|
||||
texture2d<half> colorTexture [[ texture(UTMTextureIndexBaseColor) ]],
|
||||
sampler textureSampler [[ sampler(UTMSamplerIndexTexture) ]])
|
||||
sampler textureSampler [[ sampler(UTMSamplerIndexTexture) ]],
|
||||
constant bool *isInverted [[ buffer(UTMFragmentBufferIndexIsInverted) ]])
|
||||
{
|
||||
// Sample the texture to obtain a color
|
||||
half4 colorSample = colorTexture.sample(textureSampler, in.textureCoordinate);
|
||||
@@ -95,7 +96,7 @@ samplingShader(RasterizerData in [[stage_in]],
|
||||
colorSample.a = 0xff;
|
||||
}
|
||||
|
||||
// We return the color of the texture inverted
|
||||
return float4(colorSample);
|
||||
// We return the color of the texture inverted when requested
|
||||
return float4(*isInverted ? colorSample.bgra : colorSample);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,9 +36,9 @@
|
||||
835AA7B126AB7C85007A0411 /* UTMPendingVirtualMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 835AA7B026AB7C85007A0411 /* UTMPendingVirtualMachine.swift */; };
|
||||
835AA7B226AB7C85007A0411 /* UTMPendingVirtualMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 835AA7B026AB7C85007A0411 /* UTMPendingVirtualMachine.swift */; };
|
||||
835AA7B326AB7C85007A0411 /* UTMPendingVirtualMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 835AA7B026AB7C85007A0411 /* UTMPendingVirtualMachine.swift */; };
|
||||
836A40D526AA2A03002068F8 /* Zip in Frameworks */ = {isa = PBXBuildFile; productRef = 836A40D426AA2A03002068F8 /* Zip */; };
|
||||
836A40D726AA2A2C002068F8 /* Zip in Frameworks */ = {isa = PBXBuildFile; productRef = 836A40D626AA2A2C002068F8 /* Zip */; };
|
||||
836A40D926AA2A30002068F8 /* Zip in Frameworks */ = {isa = PBXBuildFile; productRef = 836A40D826AA2A30002068F8 /* Zip */; };
|
||||
83993290272F4A400059355F /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 8399328F272F4A400059355F /* ZIPFoundation */; };
|
||||
83993292272F68550059355F /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 83993291272F68550059355F /* ZIPFoundation */; };
|
||||
83993294272F685F0059355F /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 83993293272F685F0059355F /* ZIPFoundation */; };
|
||||
83A004B926A8CC95001AC09E /* UTMImportFromWebTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83A004B826A8CC95001AC09E /* UTMImportFromWebTask.swift */; };
|
||||
83A004BA26A8CC95001AC09E /* UTMImportFromWebTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83A004B826A8CC95001AC09E /* UTMImportFromWebTask.swift */; };
|
||||
83A004BB26A8CC95001AC09E /* UTMImportFromWebTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83A004B826A8CC95001AC09E /* UTMImportFromWebTask.swift */; };
|
||||
@@ -69,6 +69,8 @@
|
||||
84FCABBA268CE05E0036196C /* UTMQemuVirtualMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = 84FCABB9268CE05E0036196C /* UTMQemuVirtualMachine.m */; };
|
||||
84FCABBB268CE05E0036196C /* UTMQemuVirtualMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = 84FCABB9268CE05E0036196C /* UTMQemuVirtualMachine.m */; };
|
||||
84FCABBC268CE05E0036196C /* UTMQemuVirtualMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = 84FCABB9268CE05E0036196C /* UTMQemuVirtualMachine.m */; };
|
||||
B329049C270FE136002707AC /* AltKit in Frameworks */ = {isa = PBXBuildFile; productRef = B329049B270FE136002707AC /* AltKit */; };
|
||||
B3DDF57226E9BBA300CE47F0 /* AltKit in Frameworks */ = {isa = PBXBuildFile; productRef = B3DDF57126E9BBA300CE47F0 /* AltKit */; };
|
||||
CE020BA324AEDC7C00B44AB6 /* UTMData.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE020BA224AEDC7C00B44AB6 /* UTMData.swift */; };
|
||||
CE020BA424AEDC7C00B44AB6 /* UTMData.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE020BA224AEDC7C00B44AB6 /* UTMData.swift */; };
|
||||
CE020BA724AEDEF000B44AB6 /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = CE020BA624AEDEF000B44AB6 /* Logging */; };
|
||||
@@ -2212,6 +2214,7 @@
|
||||
CE2D933724AD46670059923A /* libgstplayback.a in Frameworks */,
|
||||
CE2D933824AD46670059923A /* libgstadder.a in Frameworks */,
|
||||
CE2D933A24AD46670059923A /* libgstaudiorate.a in Frameworks */,
|
||||
B3DDF57226E9BBA300CE47F0 /* AltKit in Frameworks */,
|
||||
CE2D933B24AD46670059923A /* libgstvideofilter.a in Frameworks */,
|
||||
CE2D933C24AD46670059923A /* libgstapp.a in Frameworks */,
|
||||
CE2D933D24AD46670059923A /* libgstgio.a in Frameworks */,
|
||||
@@ -2220,11 +2223,13 @@
|
||||
CE2D934024AD46670059923A /* libgstosxaudio.a in Frameworks */,
|
||||
CE2D934124AD46670059923A /* gmodule-2.0.0.framework in Frameworks */,
|
||||
CE2D934224AD46670059923A /* jpeg.62.framework in Frameworks */,
|
||||
83993292272F68550059355F /* ZIPFoundation in Frameworks */,
|
||||
CE2D934324AD46670059923A /* intl.8.framework in Frameworks */,
|
||||
CE2D934424AD46670059923A /* gstapp-1.0.0.framework in Frameworks */,
|
||||
CE2D934524AD46670059923A /* gthread-2.0.0.framework in Frameworks */,
|
||||
CE2D934624AD46670059923A /* gstrtp-1.0.0.framework in Frameworks */,
|
||||
CE2D934724AD46670059923A /* gstriff-1.0.0.framework in Frameworks */,
|
||||
B329049C270FE136002707AC /* AltKit in Frameworks */,
|
||||
CEA9058F25FC6A1400801E7C /* usbredirhost.1.framework in Frameworks */,
|
||||
CE2D934924AD46670059923A /* gstreamer-1.0.0.framework in Frameworks */,
|
||||
CE2D934B24AD46670059923A /* json-glib-1.0.0.framework in Frameworks */,
|
||||
@@ -2235,7 +2240,6 @@
|
||||
CE2D934F24AD46670059923A /* phodav-2.0.0.framework in Frameworks */,
|
||||
CEA9059025FC6A1700801E7C /* usbredirparser.1.framework in Frameworks */,
|
||||
CE0E9B87252FD06B0026E02B /* SwiftUI.framework in Frameworks */,
|
||||
836A40D726AA2A2C002068F8 /* Zip in Frameworks */,
|
||||
CE2D935024AD46670059923A /* gstcontroller-1.0.0.framework in Frameworks */,
|
||||
CE2D935124AD46670059923A /* gstaudio-1.0.0.framework in Frameworks */,
|
||||
CE2D935224AD46670059923A /* gpg-error.0.framework in Frameworks */,
|
||||
@@ -2322,12 +2326,12 @@
|
||||
CE03D08924D90F0700F76B84 /* glib-2.0.0.framework in Frameworks */,
|
||||
CE0B6EC224AD677200FE012D /* libgstvideorate.a in Frameworks */,
|
||||
CE0B6F0C24AD677200FE012D /* gstreamer-1.0.0.framework in Frameworks */,
|
||||
836A40D526AA2A03002068F8 /* Zip in Frameworks */,
|
||||
CEF83F89250094A400557D15 /* png16.16.framework in Frameworks */,
|
||||
CE0B6F0224AD677200FE012D /* libgstjpeg.a in Frameworks */,
|
||||
CE0B6EFC24AD677200FE012D /* libgstaudiotestsrc.a in Frameworks */,
|
||||
CE0B6EF824AD677200FE012D /* gstsdp-1.0.0.framework in Frameworks */,
|
||||
CE0B6EEA24AD677200FE012D /* libgstcoreelements.a in Frameworks */,
|
||||
83993290272F4A400059355F /* ZIPFoundation in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -2396,12 +2400,12 @@
|
||||
CEA45F59263519B5002FA97D /* gstpbutils-1.0.0.framework in Frameworks */,
|
||||
CEA45F5A263519B5002FA97D /* gstallocators-1.0.0.framework in Frameworks */,
|
||||
CEA45F5B263519B5002FA97D /* gstcheck-1.0.0.framework in Frameworks */,
|
||||
836A40D926AA2A30002068F8 /* Zip in Frameworks */,
|
||||
CEA45F5C263519B5002FA97D /* iconv.2.framework in Frameworks */,
|
||||
CEA45F5D263519B5002FA97D /* gstsdp-1.0.0.framework in Frameworks */,
|
||||
CEA45F5E263519B5002FA97D /* ssl.1.1.framework in Frameworks */,
|
||||
CEA45F5F263519B5002FA97D /* spice-server.1.framework in Frameworks */,
|
||||
CEA45F60263519B5002FA97D /* pixman-1.0.framework in Frameworks */,
|
||||
83993294272F685F0059355F /* ZIPFoundation in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -3330,7 +3334,8 @@
|
||||
packageProductDependencies = (
|
||||
CE020BA624AEDEF000B44AB6 /* Logging */,
|
||||
CE93759824BB821F0074066F /* IQKeyboardManagerSwift */,
|
||||
836A40D626AA2A2C002068F8 /* Zip */,
|
||||
83993291272F68550059355F /* ZIPFoundation */,
|
||||
B329049B270FE136002707AC /* AltKit */,
|
||||
);
|
||||
productName = UTM;
|
||||
productReference = CE2D93BE24AD46670059923A /* UTM.app */;
|
||||
@@ -3355,7 +3360,7 @@
|
||||
name = macOS;
|
||||
packageProductDependencies = (
|
||||
CE020BA824AEDF3000B44AB6 /* Logging */,
|
||||
836A40D426AA2A03002068F8 /* Zip */,
|
||||
8399328F272F4A400059355F /* ZIPFoundation */,
|
||||
);
|
||||
productName = UTM;
|
||||
productReference = CE2D951C24AD48BE0059923A /* UTM.app */;
|
||||
@@ -3397,7 +3402,7 @@
|
||||
packageProductDependencies = (
|
||||
CEA45E20263519B5002FA97D /* Logging */,
|
||||
CEA45E22263519B5002FA97D /* IQKeyboardManagerSwift */,
|
||||
836A40D826AA2A30002068F8 /* Zip */,
|
||||
83993293272F685F0059355F /* ZIPFoundation */,
|
||||
);
|
||||
productName = UTM;
|
||||
productReference = CEA45FB9263519B5002FA97D /* UTM SE.app */;
|
||||
@@ -3465,7 +3470,8 @@
|
||||
packageReferences = (
|
||||
CE020BA524AEDEF000B44AB6 /* XCRemoteSwiftPackageReference "swift-log" */,
|
||||
CE93759724BB821F0074066F /* XCRemoteSwiftPackageReference "IQKeyboardManager" */,
|
||||
836A40D326AA2A03002068F8 /* XCRemoteSwiftPackageReference "Zip" */,
|
||||
8399328E272F4A400059355F /* XCRemoteSwiftPackageReference "ZIPFoundation" */,
|
||||
B329049A270FE136002707AC /* XCRemoteSwiftPackageReference "AltKit" */,
|
||||
);
|
||||
productRefGroup = CE550BCA225947990063E575 /* Products */;
|
||||
projectDirPath = "";
|
||||
@@ -5107,12 +5113,20 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
836A40D326AA2A03002068F8 /* XCRemoteSwiftPackageReference "Zip" */ = {
|
||||
8399328E272F4A400059355F /* XCRemoteSwiftPackageReference "ZIPFoundation" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/marmelroy/Zip.git";
|
||||
repositoryURL = "https://github.com/weichsel/ZIPFoundation.git";
|
||||
requirement = {
|
||||
kind = upToNextMinorVersion;
|
||||
minimumVersion = 2.1.1;
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 0.9.9;
|
||||
};
|
||||
};
|
||||
B329049A270FE136002707AC /* XCRemoteSwiftPackageReference "AltKit" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/utmapp/AltKit.git";
|
||||
requirement = {
|
||||
branch = main;
|
||||
kind = branch;
|
||||
};
|
||||
};
|
||||
CE020BA524AEDEF000B44AB6 /* XCRemoteSwiftPackageReference "swift-log" */ = {
|
||||
@@ -5127,8 +5141,8 @@
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/hackiftekhar/IQKeyboardManager.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 6.5.5;
|
||||
kind = exactVersion;
|
||||
version = 6.5.6;
|
||||
};
|
||||
};
|
||||
CEA45E21263519B5002FA97D /* XCRemoteSwiftPackageReference "swift-log" */ = {
|
||||
@@ -5144,26 +5158,36 @@
|
||||
repositoryURL = "https://github.com/hackiftekhar/IQKeyboardManager.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 6.5.5;
|
||||
minimumVersion = 6.5.6;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
836A40D426AA2A03002068F8 /* Zip */ = {
|
||||
8399328F272F4A400059355F /* ZIPFoundation */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 836A40D326AA2A03002068F8 /* XCRemoteSwiftPackageReference "Zip" */;
|
||||
productName = Zip;
|
||||
package = 8399328E272F4A400059355F /* XCRemoteSwiftPackageReference "ZIPFoundation" */;
|
||||
productName = ZIPFoundation;
|
||||
};
|
||||
836A40D626AA2A2C002068F8 /* Zip */ = {
|
||||
83993291272F68550059355F /* ZIPFoundation */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 836A40D326AA2A03002068F8 /* XCRemoteSwiftPackageReference "Zip" */;
|
||||
productName = Zip;
|
||||
package = 8399328E272F4A400059355F /* XCRemoteSwiftPackageReference "ZIPFoundation" */;
|
||||
productName = ZIPFoundation;
|
||||
};
|
||||
836A40D826AA2A30002068F8 /* Zip */ = {
|
||||
83993293272F685F0059355F /* ZIPFoundation */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 836A40D326AA2A03002068F8 /* XCRemoteSwiftPackageReference "Zip" */;
|
||||
productName = Zip;
|
||||
package = 8399328E272F4A400059355F /* XCRemoteSwiftPackageReference "ZIPFoundation" */;
|
||||
productName = ZIPFoundation;
|
||||
};
|
||||
B329049B270FE136002707AC /* AltKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = B329049A270FE136002707AC /* XCRemoteSwiftPackageReference "AltKit" */;
|
||||
productName = AltKit;
|
||||
};
|
||||
B3DDF57126E9BBA300CE47F0 /* AltKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = B3DDF57026E9BBA300CE47F0 /* XCRemoteSwiftPackageReference "AltKit" */;
|
||||
productName = AltKit;
|
||||
};
|
||||
CE020BA624AEDEF000B44AB6 /* Logging */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"object": {
|
||||
"pins": [
|
||||
{
|
||||
"package": "AltKit",
|
||||
"repositoryURL": "https://github.com/utmapp/AltKit.git",
|
||||
"state": {
|
||||
"branch": "main",
|
||||
"revision": "71ec1d505cbb7cb53dc16c7c98cb94c93953f3ff",
|
||||
"version": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "IQKeyboardManagerSwift",
|
||||
"repositoryURL": "https://github.com/hackiftekhar/IQKeyboardManager.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "4dc6bc7d1c9747bd0c261c8c055903f9fdef91f7",
|
||||
"version": "6.5.6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "swift-log",
|
||||
"repositoryURL": "https://github.com/apple/swift-log",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "5d66f7ba25daf4f94100e7022febf3c75e37a6c7",
|
||||
"version": "1.4.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "ZIPFoundation",
|
||||
"repositoryURL": "https://github.com/weichsel/ZIPFoundation.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "cf10bbff6ac3b873e97b36b9784c79866a051a8e",
|
||||
"version": "0.9.12"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -228,3 +228,29 @@ index ca8a4e7f..8265f15e 100644
|
||||
--
|
||||
2.28.0
|
||||
|
||||
From 3977c49109906b9ab26950e301a5ad684c36a57d Mon Sep 17 00:00:00 2001
|
||||
From: osy <[email protected]>
|
||||
Date: Tue, 2 Nov 2021 12:56:54 -0700
|
||||
Subject: [PATCH] reds: always send monitors config
|
||||
|
||||
Windows VDagent needs this to apply the resolution changes.
|
||||
---
|
||||
server/reds.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/server/reds.c b/server/reds.c
|
||||
index f1e19b87..2fa2cfae 100644
|
||||
--- a/server/reds.c
|
||||
+++ b/server/reds.c
|
||||
@@ -1182,7 +1182,7 @@ void reds_on_main_agent_data(RedsState *reds, MainChannelClient *mcc, const void
|
||||
return;
|
||||
case AGENT_MSG_FILTER_MONITORS_CONFIG:
|
||||
reds_on_main_agent_monitors_config(reds, mcc, message, size);
|
||||
- return;
|
||||
+ break;
|
||||
case AGENT_MSG_FILTER_PROTO_ERROR:
|
||||
red_channel_client_shutdown(RED_CHANNEL_CLIENT(mcc));
|
||||
return;
|
||||
--
|
||||
2.28.0
|
||||
|
||||
|
||||
+19
-14
@@ -28,14 +28,17 @@ fi
|
||||
MODE=$1
|
||||
INPUT=$2
|
||||
OUTPUT=$3
|
||||
BUNDLE_ID=
|
||||
|
||||
case $MODE in
|
||||
deb | ipa | signedipa )
|
||||
NAME="UTM"
|
||||
BUNDLE_ID="com.utmapp.UTM"
|
||||
INPUT_APP="$INPUT/Products/Applications/UTM.app"
|
||||
;;
|
||||
ipa-se )
|
||||
NAME="UTM SE"
|
||||
BUNDLE_ID="com.utmapp.UTM-SE"
|
||||
INPUT_APP="$INPUT/Products/Applications/UTM SE.app"
|
||||
;;
|
||||
* )
|
||||
@@ -76,7 +79,7 @@ itunes_sign() {
|
||||
<string>development</string>
|
||||
<key>provisioningProfiles</key>
|
||||
<dict>
|
||||
<key>com.utmapp.UTM</key>
|
||||
<key>${BUNDLE_ID}</key>
|
||||
<string>${PROFILE_NAME}</string>
|
||||
</dict>
|
||||
<key>signingStyle</key>
|
||||
@@ -97,14 +100,15 @@ EOL
|
||||
|
||||
fake_sign() {
|
||||
local _name=$1
|
||||
local _input=$2
|
||||
local _output=$3
|
||||
local _fakeent=$4
|
||||
local _bundle_id=$2
|
||||
local _input=$3
|
||||
local _output=$4
|
||||
local _fakeent=$5
|
||||
|
||||
mkdir -p "$_output"
|
||||
cp -a "$_input" "$_output/"
|
||||
find "$_output" -type f \( -path '*/Frameworks/*.framework/*' -and -not -name 'Info.plist' \) -exec ldid -S \{\} \;
|
||||
ldid -S${_fakeent} "$_output/Applications/$_name.app/$_name"
|
||||
find "$_output" -type d -path '*/Frameworks/*.framework' -exec ldid -S \{\} \;
|
||||
ldid -S${_fakeent} -I${_bundle_id} "$_output/Applications/$_name.app/$_name"
|
||||
}
|
||||
|
||||
create_deb() {
|
||||
@@ -145,21 +149,22 @@ EOL
|
||||
strip "$DEB_TMP/DEBIAN/prerm"
|
||||
ldid -S"$BASEDIR/deb/prerm.xml" "$DEB_TMP/DEBIAN/prerm"
|
||||
mkdir -p "$IPA_PATH"
|
||||
create_fake_ipa "UTM" "$INPUT" "$IPA_PATH" "$FAKEENT"
|
||||
create_fake_ipa "UTM" "com.utmapp.UTM" "$INPUT" "$IPA_PATH" "$FAKEENT"
|
||||
dpkg-deb -b -Zgzip -z9 "$DEB_TMP" "$OUTPUT/UTM.deb"
|
||||
rm -r "$DEB_TMP"
|
||||
}
|
||||
|
||||
create_fake_ipa() {
|
||||
local NAME=$1
|
||||
local INPUT=$2
|
||||
local OUTPUT=$3
|
||||
local FAKEENT=$4
|
||||
local BUNDLE_ID=$2
|
||||
local INPUT=$3
|
||||
local OUTPUT=$4
|
||||
local FAKEENT=$5
|
||||
|
||||
pwd="$(pwd)"
|
||||
mkdir -p "$OUTPUT"
|
||||
rm -rf "$OUTPUT/Applications" "$OUTPUT/Payload" "$OUTPUT/UTM.ipa"
|
||||
fake_sign "$NAME" "$INPUT/Products/Applications" "$OUTPUT" "$FAKEENT"
|
||||
fake_sign "$NAME" "$BUNDLE_ID" "$INPUT/Products/Applications" "$OUTPUT" "$FAKEENT"
|
||||
mv "$OUTPUT/Applications" "$OUTPUT/Payload"
|
||||
cd "$OUTPUT"
|
||||
zip -r "$NAME.ipa" "Payload" -x "._*" -x ".DS_Store" -x "__MACOSX"
|
||||
@@ -226,7 +231,7 @@ ipa )
|
||||
</dict>
|
||||
</plist>
|
||||
EOL
|
||||
create_fake_ipa "$NAME" "$INPUT" "$OUTPUT" "$FAKEENT"
|
||||
create_fake_ipa "$NAME" "$BUNDLE_ID" "$INPUT" "$OUTPUT" "$FAKEENT"
|
||||
rm "$FAKEENT"
|
||||
;;
|
||||
ipa-se )
|
||||
@@ -241,9 +246,9 @@ ipa-se )
|
||||
</dict>
|
||||
</plist>
|
||||
EOL
|
||||
create_fake_ipa "$NAME" "$INPUT" "$OUTPUT" "$FAKEENT"
|
||||
create_fake_ipa "$NAME" "$BUNDLE_ID" "$INPUT" "$OUTPUT" "$FAKEENT"
|
||||
;;
|
||||
signedipa )
|
||||
itunes_sign "$INPUT" "$OUTPUT" $4 $5
|
||||
itunes_sign "$INPUT" "$OUTPUT" $5 $6
|
||||
;;
|
||||
esac
|
||||
|
||||
Reference in New Issue
Block a user