Merge tag 'v2.4.1' into dev-monterey

This commit is contained in:
osy
2021-11-27 16:50:32 -08:00
24 changed files with 346 additions and 110 deletions
+2 -2
View File
@@ -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"
+4
View File
@@ -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
View File
@@ -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);
}
+5 -2
View File
@@ -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 {
+4
View File
@@ -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
+63 -26
View File
@@ -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
View File
@@ -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
}
+8
View File
@@ -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" = "虛擬機背景存取權需要位置服務。位置資料不會傳出手機。";
+1 -1
View File
@@ -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 {
-1
View File
@@ -12,4 +12,3 @@
/* (No Comment) */
"UTM virtual machine" = "UTM虚拟机";
+6 -3
View File
@@ -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" = "随机生成";
+3 -2
View File
@@ -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
+1
View File
@@ -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;
+9
View File
@@ -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];
+5
View File
@@ -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
+4 -3
View File
@@ -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);
}
+50 -26
View File
@@ -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
}
+26
View File
@@ -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
View File
@@ -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