keyboard for visionOS

This commit is contained in:
Jesús A. Álvarez 2024-02-10 12:15:01 +01:00
parent 4aef44bf76
commit 7c1f280374
46 changed files with 466 additions and 18 deletions

View File

@ -58,6 +58,9 @@
28C67BE92AC49E46000C7540 /* Icon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 283423EE1CFA329C0088B634 /* Icon@3x.png */; };
28C67BF62AC49FA1000C7540 /* MacII-512x384.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 28C67BEE2AC49E46000C7540 /* MacII-512x384.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
28CE8ED61CD4F56C00FE25A8 /* ScreenView.m in Sources */ = {isa = PBXBuildFile; fileRef = 28CE8ED51CD4F56C00FE25A8 /* ScreenView.m */; };
28D3C6152B7681420079E915 /* VisionSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28D3C6142B7681420079E915 /* VisionSupport.swift */; };
28D3C6172B76B8970079E915 /* DefaultSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28D3C6162B76B8970079E915 /* DefaultSceneDelegate.swift */; };
28D3C61B2B7781700079E915 /* KeyboardSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28D3C61A2B7781700079E915 /* KeyboardSceneDelegate.swift */; };
28D5A3FD1CD6868F001A33F6 /* TouchScreen.m in Sources */ = {isa = PBXBuildFile; fileRef = 28D5A3FC1CD6868E001A33F6 /* TouchScreen.m */; };
28E3B7DF251D0F13007C273F /* MOUSEMDV.c in Sources */ = {isa = PBXBuildFile; fileRef = 28E3B7CC251D0F12007C273F /* MOUSEMDV.c */; };
28E3B7E0251D0F13007C273F /* MOUSEMDV.c in Sources */ = {isa = PBXBuildFile; fileRef = 28E3B7CC251D0F12007C273F /* MOUSEMDV.c */; };
@ -282,6 +285,10 @@
28CE8ECB1CD4CDC500FE25A8 /* MYOSGLUE.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYOSGLUE.m; sourceTree = "<group>"; };
28CE8ED41CD4F56C00FE25A8 /* ScreenView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScreenView.h; sourceTree = "<group>"; };
28CE8ED51CD4F56C00FE25A8 /* ScreenView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScreenView.m; sourceTree = "<group>"; };
28D3C6132B7681420079E915 /* Mini vMac-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Mini vMac-Bridging-Header.h"; sourceTree = "<group>"; };
28D3C6142B7681420079E915 /* VisionSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisionSupport.swift; sourceTree = "<group>"; };
28D3C6162B76B8970079E915 /* DefaultSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultSceneDelegate.swift; sourceTree = "<group>"; };
28D3C61A2B7781700079E915 /* KeyboardSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardSceneDelegate.swift; sourceTree = "<group>"; };
28D5A3FB1CD6868E001A33F6 /* TouchScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TouchScreen.h; sourceTree = "<group>"; };
28D5A3FC1CD6868E001A33F6 /* TouchScreen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TouchScreen.m; sourceTree = "<group>"; };
28E3B7CC251D0F12007C273F /* MOUSEMDV.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = MOUSEMDV.c; sourceTree = "<group>"; };
@ -611,6 +618,7 @@
28BA89841CE73E7200A98104 /* TrackPad.m */,
28F676C61CD15E0B00FC6FA6 /* ViewController.h */,
28F676C71CD15E0B00FC6FA6 /* ViewController.m */,
28D3C6142B7681420079E915 /* VisionSupport.swift */,
28848B601CDE97D600B86C45 /* InsertDiskViewController.h */,
28848B611CDE97D600B86C45 /* InsertDiskViewController.m */,
28848B631CDE97E900B86C45 /* SettingsViewController.h */,
@ -627,6 +635,8 @@
28F6B4CE1CF77099002D76D0 /* compat.m */,
283CA9821DF47AF300B33D5E /* BTCMouse.h */,
283422EF1CF8F33A0088B634 /* Emulator Bundles */,
28D3C6162B76B8970079E915 /* DefaultSceneDelegate.swift */,
28D3C61A2B7781700079E915 /* KeyboardSceneDelegate.swift */,
);
path = "Mini vMac";
sourceTree = "<group>";
@ -634,6 +644,7 @@
28F676C01CD15E0B00FC6FA6 /* Supporting Files */ = {
isa = PBXGroup;
children = (
28D3C6132B7681420079E915 /* Mini vMac-Bridging-Header.h */,
28F6B4551CF07C9A002D76D0 /* libhfs */,
28F6B4541CF07C8D002D76D0 /* libmfs */,
28F6B4531CF07C83002D76D0 /* libres */,
@ -950,6 +961,7 @@
28F676BC1CD15E0B00FC6FA6 = {
CreatedOnToolsVersion = 7.3;
DevelopmentTeam = UJXNDZ5TNU;
LastSwiftMigration = 1520;
};
28F6B48D1CF07DDD002D76D0 = {
CreatedOnToolsVersion = 7.3.1;
@ -1287,14 +1299,17 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
28D3C6152B7681420079E915 /* VisionSupport.swift in Sources */,
28BA897E1CE7315400A98104 /* KBKey.m in Sources */,
28BA89851CE73E7200A98104 /* TrackPad.m in Sources */,
28D3C6172B76B8970079E915 /* DefaultSceneDelegate.swift in Sources */,
28CE8ED61CD4F56C00FE25A8 /* ScreenView.m in Sources */,
28848B651CDE97E900B86C45 /* SettingsViewController.m in Sources */,
28BA89881CE73FBC00A98104 /* MNVMApplication.m in Sources */,
28F6B4CF1CF77099002D76D0 /* compat.m in Sources */,
28F6B4521CF07C48002D76D0 /* UIImage+DiskImageIcon.m in Sources */,
28BA897F1CE7315400A98104 /* KBKeyboardLayout.m in Sources */,
28D3C61B2B7781700079E915 /* KeyboardSceneDelegate.swift in Sources */,
28848B621CDE97D600B86C45 /* InsertDiskViewController.m in Sources */,
28F676C81CD15E0B00FC6FA6 /* ViewController.m in Sources */,
28D5A3FD1CD6868F001A33F6 /* TouchScreen.m in Sources */,
@ -1902,6 +1917,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 14;
@ -1921,6 +1937,9 @@
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Mini vMac/Mini vMac-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2,7";
};
name = Debug;
@ -1929,6 +1948,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 14;
@ -1948,6 +1968,8 @@
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Mini vMac/Mini vMac-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2,7";
};
name = Release;

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:Mini vMac.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,37 @@
{
"DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "9C19786155D97B8ED78AB84AFAAA50249126341A",
"DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
},
"DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
"9C19786155D97B8ED78AB84AFAAA50249126341A" : 0,
"F2E8D120DACD6BF5E1EFFA012C92CE6FA0E44B4C" : 0,
"45D35ABA2A883CFEB6F6014CF9723CDA568B602B" : 0
},
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "A273011D-9F4F-4F93-8287-77CF2E175709",
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
"9C19786155D97B8ED78AB84AFAAA50249126341A" : "minivmac4ios\/",
"F2E8D120DACD6BF5E1EFFA012C92CE6FA0E44B4C" : "minivmac4ios\/libres\/",
"45D35ABA2A883CFEB6F6014CF9723CDA568B602B" : "minivmac4ios\/libmfs\/"
},
"DVTSourceControlWorkspaceBlueprintNameKey" : "Mini vMac",
"DVTSourceControlWorkspaceBlueprintVersion" : 204,
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "Mini vMac.xcodeproj",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/zydeco\/libmfs.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "45D35ABA2A883CFEB6F6014CF9723CDA568B602B"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:zydeco\/minivmac4ios.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "9C19786155D97B8ED78AB84AFAAA50249126341A"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/zydeco\/libres.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "F2E8D120DACD6BF5E1EFFA012C92CE6FA0E44B4C"
}
]
}

View File

@ -10,6 +10,7 @@
#import "AppDelegate.h"
#import "SettingsViewController.h"
#import "InsertDiskViewController.h"
#import "ViewController.h"
static AppDelegate *sharedAppDelegate = nil;
static NSObject<Emulator> *sharedEmulator = nil;
@ -106,6 +107,9 @@ NSString *DocumentsChangedNotification = @"documentsChanged";
[self showAlertWithTitle:title message:message];
};
sharedEmulator.dataPath = self.documentsPath;
#if defined(TARGET_OS_VISION) && TARGET_OS_VISION == 1
[ViewController adjustToScreenSize];
#endif
return sharedEmulator != nil;
}
@ -311,4 +315,36 @@ NSString *DocumentsChangedNotification = @"documentsChanged";
return YES;
}
#pragma mark - Making a Scene
- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {
for (NSUserActivity *activity in options.userActivities) {
if ([activity.activityType isEqualToString:@"net.namedfork.keyboard"]) {
return [UISceneConfiguration configurationWithName:@"Keyboard" sessionRole:UIWindowSceneSessionRoleApplication];
}
}
if (![self hasDefaultScene]) {
return [UISceneConfiguration configurationWithName:@"Default" sessionRole:UIWindowSceneSessionRoleApplication];
}
return nil;
}
- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions {
// if only keyboard is left, show default view again
if (![self hasDefaultScene]) {
UISceneSessionActivationRequest *request = [UISceneSessionActivationRequest requestWithRole:UIWindowSceneSessionRoleApplication];
[application activateSceneSessionForRequest:request errorHandler:nil];
}
}
- (BOOL)hasDefaultScene {
for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) {
if ([scene.session.configuration.name isEqualToString:@"Default"]) {
return YES;
}
}
return NO;
}
@end

View File

@ -23,6 +23,11 @@
"filename" : "KBCapsLock~ipad@2x.png",
"idiom" : "ipad",
"scale" : "2x"
},
{
"filename" : "KBCapsLock~ipad@2x 1.png",
"idiom" : "vision",
"scale" : "2x"
}
],
"info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

View File

@ -23,6 +23,11 @@
"filename" : "KBClearDown~ipad@2x.png",
"idiom" : "ipad",
"scale" : "2x"
},
{
"filename" : "KBClearDown~ipad@2x 1.png",
"idiom" : "vision",
"scale" : "2x"
}
],
"info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 B

View File

@ -23,6 +23,11 @@
"filename" : "KBClearUp~ipad@2x.png",
"idiom" : "ipad",
"scale" : "2x"
},
{
"filename" : "KBClearUp~ipad@2x 1.png",
"idiom" : "vision",
"scale" : "2x"
}
],
"info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 B

View File

@ -23,6 +23,11 @@
"filename" : "KBCommand@2x~ipad.png",
"idiom" : "ipad",
"scale" : "2x"
},
{
"filename" : "KBCommand@2x~ipad 1.png",
"idiom" : "vision",
"scale" : "2x"
}
],
"info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -23,6 +23,11 @@
"filename" : "KBDeleteDown~ipad@2x.png",
"idiom" : "ipad",
"scale" : "2x"
},
{
"filename" : "KBDeleteDown~ipad@2x 1.png",
"idiom" : "vision",
"scale" : "2x"
}
],
"info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

View File

@ -23,6 +23,11 @@
"filename" : "KBDeleteUp~ipad@2x.png",
"idiom" : "ipad",
"scale" : "2x"
},
{
"filename" : "KBDeleteUp~ipad@2x 1.png",
"idiom" : "vision",
"scale" : "2x"
}
],
"info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 B

View File

@ -23,6 +23,11 @@
"filename" : "KBForwardDeleteDown~ipad@2x.png",
"idiom" : "ipad",
"scale" : "2x"
},
{
"filename" : "KBForwardDeleteDown~ipad@2x 1.png",
"idiom" : "vision",
"scale" : "2x"
}
],
"info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

View File

@ -23,6 +23,11 @@
"filename" : "KBForwardDeleteUp~ipad@2x.png",
"idiom" : "ipad",
"scale" : "2x"
},
{
"filename" : "KBForwardDeleteUp~ipad@2x 1.png",
"idiom" : "vision",
"scale" : "2x"
}
],
"info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 B

View File

@ -1,6 +1,7 @@
{
"images" : [
{
"filename" : "KBHide~ipad 2.png",
"idiom" : "iphone",
"scale" : "1x"
},
@ -23,6 +24,11 @@
"filename" : "KBHide~ipad@2x.png",
"idiom" : "ipad",
"scale" : "2x"
},
{
"filename" : "KBHide~ipad@2x 1.png",
"idiom" : "vision",
"scale" : "2x"
}
],
"info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 B

View File

@ -219,6 +219,25 @@
"mode" : "9-part"
},
"scale" : "2x"
},
{
"filename" : "KBKey@2x~ipad-2 1.png",
"idiom" : "vision",
"resizing" : {
"cap-insets" : {
"bottom" : 32,
"left" : 32,
"right" : 32,
"top" : 32
},
"center" : {
"height" : 2,
"mode" : "tile",
"width" : 2
},
"mode" : "9-part"
},
"scale" : "2x"
}
],
"info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

View File

@ -219,6 +219,25 @@
"mode" : "9-part"
},
"scale" : "2x"
},
{
"filename" : "KBKeyDark@2x~ipad-1 1.png",
"idiom" : "vision",
"resizing" : {
"cap-insets" : {
"bottom" : 32,
"left" : 32,
"right" : 32,
"top" : 32
},
"center" : {
"height" : 2,
"mode" : "tile",
"width" : 2
},
"mode" : "9-part"
},
"scale" : "2x"
}
],
"info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

View File

@ -23,6 +23,11 @@
"filename" : "KBNumPad@2x~ipad.png",
"idiom" : "ipad",
"scale" : "2x"
},
{
"filename" : "KBNumPad@2x~ipad 1.png",
"idiom" : "vision",
"scale" : "2x"
}
],
"info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

View File

@ -23,6 +23,11 @@
"filename" : "KBOption@2x~ipad.png",
"idiom" : "ipad",
"scale" : "2x"
},
{
"filename" : "KBOption@2x~ipad 1.png",
"idiom" : "vision",
"scale" : "2x"
}
],
"info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

View File

@ -23,6 +23,11 @@
"filename" : "KBShiftDown~ipad@2x.png",
"idiom" : "ipad",
"scale" : "2x"
},
{
"filename" : "KBShiftDown~ipad@2x 1.png",
"idiom" : "vision",
"scale" : "2x"
}
],
"info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

View File

@ -23,6 +23,11 @@
"filename" : "KBShiftUp~ipad@2x.png",
"idiom" : "ipad",
"scale" : "2x"
},
{
"filename" : "KBShiftUp~ipad@2x 1.png",
"idiom" : "vision",
"scale" : "2x"
}
],
"info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 B

View File

@ -1,23 +1,26 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "reset.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "reset@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "reset@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
}

View File

@ -1,23 +1,26 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "Settings.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Settings@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Settings@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
}

View File

@ -0,0 +1,35 @@
//
// DefaultSceneDelegate.swift
// Mini vMac
//
// Created by Jesús A. Álvarez on 2024-02-09.
// Copyright © 2024 namedfork. All rights reserved.
//
import UIKit
class DefaultSceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = scene as? UIWindowScene else {
fatalError("Expected scene of type UIWindowScene but got an unexpected type")
}
guard let appDelegate = AppDelegate.sharedInstance() else {
fatalError("No app delegate")
}
let size = CGSize(width: 1024.0, height: 684.0)
windowScene.sizeRestrictions?.minimumSize = size
windowScene.sizeRestrictions?.maximumSize = size
window = UIWindow(windowScene: windowScene)
let rootViewController = appDelegate.window?.rootViewController ?? UIStoryboard(name: "Main", bundle: .main).instantiateInitialViewController()
if let window {
appDelegate.window = window
window.rootViewController = rootViewController
window.makeKeyAndVisible()
}
}
}

View File

@ -176,5 +176,28 @@
</dict>
</dict>
</array>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).DefaultSceneDelegate</string>
</dict>
<dict>
<key>UISceneConfigurationName</key>
<string>Keyboard</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).KeyboardSceneDelegate</string>
</dict>
</array>
</dict>
</dict>
</dict>
</plist>

View File

@ -126,8 +126,13 @@
keyFrame.origin.x += safeAreaInsets.left;
}
if (scancode == VKC_HIDE) {
#if defined(TARGET_OS_VISION) && TARGET_OS_VISION == 1
// close window to hide
return;
#else
key = [[KBHideKey alloc] initWithFrame:keyFrame];
[key addTarget:self action:@selector(hideKeyboard:) forControlEvents:UIControlEventTouchUpInside];
#endif
} else if (scancode == VKC_SHIFT_CAPS) {
key = [[KBShiftCapsKey alloc] initWithFrame:keyFrame];
key.scancode = KC_SHIFT;

View File

@ -0,0 +1,32 @@
//
// KeyboardSceneDelegate.swift
// Mini vMac
//
// Created by Jesús A. Álvarez on 2024-02-10.
// Copyright © 2024 namedfork. All rights reserved.
//
import UIKit
class KeyboardSceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = scene as? UIWindowScene else {
fatalError("Expected scene of type UIWindowScene but got an unexpected type")
}
guard let mainViewController = AppDelegate.sharedInstance().window.rootViewController as? ViewController else {
fatalError("No main view controller")
}
let size = mainViewController.keyboardViewController.preferredContentSize
windowScene.sizeRestrictions?.minimumSize = size
windowScene.sizeRestrictions?.maximumSize = size
window = UIWindow(windowScene: windowScene)
if let window {
window.rootViewController = mainViewController.keyboardViewController
window.makeKeyAndVisible()
}
}
}

View File

@ -0,0 +1,8 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "ViewController.h"
#import "AppDelegate.h"
#import "KBKeyboardView.h"
#import "KBKeyboardLayout.h"

View File

@ -177,7 +177,11 @@ typedef enum : NSInteger {
case SettingsSectionMachine:
return machineList.count;
case SettingsSectionAbout:
#if !defined(TARGET_OS_VISION) || TARGET_OS_VISION == 0
return aboutItems.count + 1;
#else
return aboutItems.count;
#endif
default:
return 1;
}

View File

@ -18,6 +18,14 @@
- (IBAction)showGestureHelp:(id)sender;
- (IBAction)hideGestureHelp:(id)sender;
- (void)showKeyboard:(id)sender;
@end
#if defined(TARGET_OS_VISION) && TARGET_OS_VISION == 1
@interface ViewController (VisionSupport)
@property (nonatomic, readonly) UIViewController* keyboardViewController;
- (void)initXr;
+ (void)adjustToScreenSize;
@end
#endif

View File

@ -29,6 +29,7 @@ API_AVAILABLE(ios(13.4))
KBKeyboardView *keyboardView;
UISwipeGestureRecognizer *showKeyboardGesture, *hideKeyboardGesture, *insertDiskGesture, *showSettingsGesture;
UIControl *pointingDeviceView;
UIViewController *_keyboardViewController;
id interaction;
}
@ -43,20 +44,55 @@ API_AVAILABLE(ios(13.4))
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(emulatorDidShutDown:) name:[AppDelegate sharedEmulator].shutdownNotification object:nil];
#if defined(TARGET_OS_VISION) && TARGET_OS_VISION == 1
[self initXr];
#else
[self scheduleHelpPresentationIfNeededAfterDelay:6.0];
[self installGestures];
#endif
}
#if defined(TARGET_OS_VISION) && TARGET_OS_VISION == 1
- (UIViewController *)keyboardViewController {
if (keyboardView == nil) {
[[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:@"keyboardLayout" options:0 context:NULL];
KBKeyboardLayout *layout = [self keyboardLayout];
CGSize keyboardSize = CGSizeZero;
for (NSValue *size in layout.availableSizes) {
if (size.CGSizeValue.width > keyboardSize.width) {
keyboardSize = size.CGSizeValue;
}
}
keyboardView = [[KBKeyboardView alloc] initWithFrame:CGRectMake(0, 0, keyboardSize.width, keyboardSize.height)];
keyboardView.layout = layout;
keyboardView.delegate = self;
}
if (_keyboardViewController == nil) {
_keyboardViewController = [UIViewController alloc];
_keyboardViewController.view = keyboardView;
_keyboardViewController.preferredContentSize = keyboardView.frame.size;
} else if (_keyboardViewController.view != keyboardView) {
_keyboardViewController.view = keyboardView;
}
return _keyboardViewController;
}
#endif
- (void)installGestures {
[self installKeyboardGestures];
insertDiskGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(showInsertDisk:)];
insertDiskGesture.direction = UISwipeGestureRecognizerDirectionLeft;
insertDiskGesture.numberOfTouchesRequired = 2;
[self.view addGestureRecognizer:insertDiskGesture];
showSettingsGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(showSettings:)];
showSettingsGesture.direction = UISwipeGestureRecognizerDirectionRight;
showSettingsGesture.numberOfTouchesRequired = 2;
[self.view addGestureRecognizer:showSettingsGesture];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(emulatorDidShutDown:) name:[AppDelegate sharedEmulator].shutdownNotification object:nil];
[self scheduleHelpPresentationIfNeededAfterDelay:6.0];
}
- (void)showSettings:(id)sender {
@ -149,6 +185,11 @@ API_AVAILABLE(ios(13.4))
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if (object == [NSUserDefaults standardUserDefaults]) {
if ([keyPath isEqualToString:@"keyboardLayout"] && keyboardView != nil) {
#if defined(TARGET_OS_VISION) && TARGET_OS_VISION == 1
// FIXME: do this nicelier
keyboardView = nil;
[self keyboardViewController];
#else
BOOL keyboardWasVisible = self.keyboardVisible;
[self setKeyboardVisible:NO animated:NO];
[keyboardView removeFromSuperview];
@ -156,6 +197,7 @@ API_AVAILABLE(ios(13.4))
if (keyboardWasVisible) {
[self setKeyboardVisible:YES animated:NO];
}
#endif
} else if ([keyPath isEqualToString:@"trackpad"]) {
[self setUpPointingDevice];
}
@ -215,7 +257,9 @@ API_AVAILABLE(ios(13.4))
#pragma mark - Gesture Help
- (void)showGestureHelp:(id)sender {
#if !defined(TARGET_OS_VISION) || TARGET_OS_VISION == 0
[self setGestureHelpHidden:NO];
#endif
}
- (void)hideGestureHelp:(id)sender {
@ -271,7 +315,11 @@ API_AVAILABLE(ios(13.4))
}
- (BOOL)isKeyboardVisible {
#if defined(TARGET_OS_VISION) && TARGET_OS_VISION == 1
return _keyboardViewController.view.window != nil;
#else
return keyboardView != nil && CGRectIntersectsRect(keyboardView.frame, self.view.bounds) && !keyboardView.hidden;
#endif
}
- (void)setKeyboardVisible:(BOOL)keyboardVisible {
@ -291,6 +339,13 @@ API_AVAILABLE(ios(13.4))
return;
}
#if defined(TARGET_OS_VISION) && TARGET_OS_VISION == 1
if (visible) {
UISceneSessionActivationRequest *request = [UISceneSessionActivationRequest requestWithRole:UIWindowSceneSessionRoleApplication];
request.userActivity = [[NSUserActivity alloc] initWithActivityType:@"net.namedfork.keyboard"];
[[UIApplication sharedApplication] activateSceneSessionForRequest:request errorHandler:nil];
} // only show, no hide
#else
if (visible) {
[[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:@"keyboardLayout" options:0 context:NULL];
[self loadKeyboardView];
@ -324,7 +379,7 @@ API_AVAILABLE(ios(13.4))
keyboardView.hidden = YES;
}
}
#endif
}
- (void)loadKeyboardView {

View File

@ -0,0 +1,66 @@
//
// VisionSupport.swift
// Mini vMac
//
// Created by Jesús A. Álvarez on 2024-02-09.
// Copyright © 2024 namedfork. All rights reserved.
//
import Foundation
import UIKit
import SwiftUI
extension ViewController {
#if os(visionOS)
@objc
func initXr() {
ViewController.adjustToScreenSize()
ornaments = [
UIHostingOrnament(sceneAnchor: .bottom, contentAlignment: .center) {
VStack {
Spacer(minLength: 80.0)
HStack {
Button(action: {
AppDelegate.sharedInstance().showSettings(self)
}, label: {
Image(systemName: "gear")
}).glassBackgroundEffect()
Button(action: {
AppDelegate.sharedInstance().showInsertDisk(self)
}, label: {
Image(systemName: "opticaldiscdrive")
}).glassBackgroundEffect()
Button(action: {
self.showKeyboard(self)
}, label: {
Image(systemName: "keyboard")
}).glassBackgroundEffect()
}.padding(.all)
.glassBackgroundEffect()
}
}
]
}
@objc
static func adjustToScreenSize() {
let screenSize = AppDelegate.sharedEmulator().screenSize
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
return
}
let minSize = screenSize
let defaultSize = screenSize.applying(.init(scaleX: 2.0, y: 2.0))
let maxSize = screenSize.applying(.init(scaleX: 3.0, y: 3.0))
windowScene.sizeRestrictions?.minimumSize = minSize
windowScene.sizeRestrictions?.maximumSize = maxSize
windowScene.requestGeometryUpdate(UIWindowScene.GeometryPreferences.Vision(
size: defaultSize,
minimumSize: minSize,
maximumSize: maxSize,
resizingRestrictions: .uniform
))
}
#endif
}