From 9ee5f012ca8b625c621dfd07767bb80a95e0bdb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesu=CC=81s=20A=2E=20A=CC=81lvarez?= Date: Sat, 14 May 2016 13:01:02 +0200 Subject: [PATCH] add emulated keyboard UI --- Mini vMac.xcodeproj/project.pbxproj | 30 ++ Mini vMac/AppDelegate.h | 3 + Mini vMac/AppDelegate.m | 34 +- .../KBCapsLock.imageset/Contents.json | 32 ++ .../KBCapsLock.imageset/KBCapsLock~ipad.png | Bin 0 -> 236 bytes .../KBCapsLock~ipad@2x.png | Bin 0 -> 317 bytes .../KBCapsLock~iphone@2x.png | Bin 0 -> 302 bytes .../KBCapsLock~iphone@3x.png | Bin 0 -> 366 bytes .../KBClearDown.imageset/Contents.json | 32 ++ .../KBClearDown.imageset/KBClearDown~ipad.png | Bin 0 -> 258 bytes .../KBClearDown~ipad@2x.png | Bin 0 -> 401 bytes .../KBClearDown~iphone@2x.png | Bin 0 -> 315 bytes .../KBClearDown~iphone@3x.png | Bin 0 -> 451 bytes .../KBClearUp.imageset/Contents.json | 32 ++ .../KBClearUp.imageset/KBClearUp~ipad.png | Bin 0 -> 345 bytes .../KBClearUp.imageset/KBClearUp~ipad@2x.png | Bin 0 -> 563 bytes .../KBClearUp.imageset/KBClearUp~iphone.png | Bin 0 -> 414 bytes .../KBClearUp~iphone@3x.png | Bin 0 -> 576 bytes .../KBCommand.imageset/Contents.json | 32 ++ .../KBCommand.imageset/KBCommand@2x~ipad.png | Bin 0 -> 1031 bytes .../KBCommand.imageset/KBCommand~ipad.png | Bin 0 -> 586 bytes .../KBCommand.imageset/KBKeyCommand@2x.png | Bin 0 -> 683 bytes .../KBCommand.imageset/KBKeyCommand@3x.png | Bin 0 -> 981 bytes .../KBDeleteDown.imageset/Contents.json | 32 ++ .../KBDeleteDown~ipad.png | Bin 0 -> 313 bytes .../KBDeleteDown~ipad@2x.png | Bin 0 -> 526 bytes .../KBDeleteDown~iphone@2x.png | Bin 0 -> 374 bytes .../KBDeleteDown~iphone@3x.png | Bin 0 -> 560 bytes .../KBDeleteUp.imageset/Contents.json | 32 ++ .../KBDeleteUp.imageset/KBDeleteUp~ipad.png | Bin 0 -> 435 bytes .../KBDeleteUp~ipad@2x.png | Bin 0 -> 726 bytes .../KBDeleteUp.imageset/KBDeleteUp~iphone.png | Bin 0 -> 493 bytes .../KBDeleteUp~iphone@3x.png | Bin 0 -> 668 bytes .../Contents.json | 32 ++ .../KBForwardDeleteDown~ipad.png | Bin 0 -> 312 bytes .../KBForwardDeleteDown~ipad@2x.png | Bin 0 -> 526 bytes .../KBForwardDeleteDown~iphone@2x.png | Bin 0 -> 375 bytes .../KBForwardDeleteDown~iphone@3x.png | Bin 0 -> 557 bytes .../KBForwardDeleteUp.imageset/Contents.json | 32 ++ .../KBForwardDeleteUp~ipad.png | Bin 0 -> 433 bytes .../KBForwardDeleteUp~ipad@2x.png | Bin 0 -> 718 bytes .../KBForwardDeleteUp~iphone.png | Bin 0 -> 492 bytes .../KBForwardDeleteUp~iphone@3x.png | Bin 0 -> 664 bytes .../KBHide.imageset/Contents.json | 32 ++ .../KBHide.imageset/KBHide~ipad.png | Bin 0 -> 242 bytes .../KBHide.imageset/KBHide~ipad@2x.png | Bin 0 -> 438 bytes .../KBHide.imageset/KBHide~iphone@2x.png | Bin 0 -> 300 bytes .../KBHide.imageset/KBHide~iphone@3x.png | Bin 0 -> 478 bytes .../KBKey.imageset/Contents.json | 103 +++++ .../Assets.xcassets/KBKey.imageset/KBKey.png | Bin 0 -> 202 bytes .../KBKey.imageset/KBKey@2x.png | Bin 0 -> 354 bytes .../KBKey.imageset/KBKey@2x~ipad.png | Bin 0 -> 574 bytes .../KBKey.imageset/KBKey@3x.png | Bin 0 -> 497 bytes .../KBKey.imageset/KBKey~ipad.png | Bin 0 -> 342 bytes .../KBKeyDark.imageset/Contents.json | 103 +++++ .../KBKeyDark.imageset/KBKeyDark.png | Bin 0 -> 214 bytes .../KBKeyDark.imageset/KBKeyDark@2x.png | Bin 0 -> 377 bytes .../KBKeyDark.imageset/KBKeyDark@2x~ipad.png | Bin 0 -> 579 bytes .../KBKeyDark.imageset/KBKeyDark@3x.png | Bin 0 -> 531 bytes .../KBKeyDark.imageset/KBKeyDark~ipad.png | Bin 0 -> 339 bytes .../KBNumPad.imageset/Contents.json | 32 ++ .../KBNumPad.imageset/KBNumPad@2x.png | Bin 0 -> 138 bytes .../KBNumPad.imageset/KBNumPad@2x~ipad.png | Bin 0 -> 178 bytes .../KBNumPad.imageset/KBNumPad@3x.png | Bin 0 -> 223 bytes .../KBNumPad.imageset/KBNumPad~ipad.png | Bin 0 -> 136 bytes .../KBOption.imageset/Contents.json | 32 ++ .../KBOption.imageset/KBKeyOption@2x.png | Bin 0 -> 172 bytes .../KBOption.imageset/KBKeyOption@3x.png | Bin 0 -> 203 bytes .../KBOption.imageset/KBOption@2x~ipad.png | Bin 0 -> 365 bytes .../KBOption.imageset/KBOption~ipad.png | Bin 0 -> 263 bytes .../KBShiftDown.imageset/Contents.json | 32 ++ .../KBShiftDown.imageset/KBShiftDown~ipad.png | Bin 0 -> 211 bytes .../KBShiftDown~ipad@2x.png | Bin 0 -> 274 bytes .../KBShiftDown~iphone@2x.png | Bin 0 -> 257 bytes .../KBShiftDown~iphone@3x.png | Bin 0 -> 314 bytes .../KBShiftUp.imageset/Contents.json | 32 ++ .../KBShiftUp.imageset/KBShiftUp~ipad.png | Bin 0 -> 305 bytes .../KBShiftUp.imageset/KBShiftUp~ipad@2x.png | Bin 0 -> 467 bytes .../KBShiftUp~iphone@2x.png | Bin 0 -> 341 bytes .../KBShiftUp~iphone@3x.png | Bin 0 -> 580 bytes Mini vMac/KBKey.h | 33 ++ Mini vMac/KBKey.m | 196 ++++++++ Mini vMac/KBKeyboardLayout.h | 26 ++ Mini vMac/KBKeyboardLayout.m | 433 ++++++++++++++++++ Mini vMac/KBKeyboardView.h | 29 ++ Mini vMac/KBKeyboardView.m | 217 +++++++++ .../Keyboard Layouts/British.nfkeyboardlayout | Bin 0 -> 2323 bytes .../Spanish (ISO).nfkeyboardlayout | Bin 0 -> 2372 bytes .../Keyboard Layouts/Spanish.nfkeyboardlayout | Bin 0 -> 2364 bytes .../Keyboard Layouts/US.nfkeyboardlayout | Bin 0 -> 2308 bytes Mini vMac/MYOSGLUE.m | 8 +- Mini vMac/ViewController.h | 4 +- Mini vMac/ViewController.m | 143 ++++++ 93 files changed, 1774 insertions(+), 4 deletions(-) create mode 100644 Mini vMac/Assets.xcassets/KBCapsLock.imageset/Contents.json create mode 100644 Mini vMac/Assets.xcassets/KBCapsLock.imageset/KBCapsLock~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBCapsLock.imageset/KBCapsLock~ipad@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBCapsLock.imageset/KBCapsLock~iphone@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBCapsLock.imageset/KBCapsLock~iphone@3x.png create mode 100644 Mini vMac/Assets.xcassets/KBClearDown.imageset/Contents.json create mode 100644 Mini vMac/Assets.xcassets/KBClearDown.imageset/KBClearDown~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBClearDown.imageset/KBClearDown~ipad@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBClearDown.imageset/KBClearDown~iphone@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBClearDown.imageset/KBClearDown~iphone@3x.png create mode 100644 Mini vMac/Assets.xcassets/KBClearUp.imageset/Contents.json create mode 100644 Mini vMac/Assets.xcassets/KBClearUp.imageset/KBClearUp~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBClearUp.imageset/KBClearUp~ipad@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBClearUp.imageset/KBClearUp~iphone.png create mode 100644 Mini vMac/Assets.xcassets/KBClearUp.imageset/KBClearUp~iphone@3x.png create mode 100644 Mini vMac/Assets.xcassets/KBCommand.imageset/Contents.json create mode 100644 Mini vMac/Assets.xcassets/KBCommand.imageset/KBCommand@2x~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBCommand.imageset/KBCommand~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBCommand.imageset/KBKeyCommand@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBCommand.imageset/KBKeyCommand@3x.png create mode 100644 Mini vMac/Assets.xcassets/KBDeleteDown.imageset/Contents.json create mode 100644 Mini vMac/Assets.xcassets/KBDeleteDown.imageset/KBDeleteDown~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBDeleteDown.imageset/KBDeleteDown~ipad@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBDeleteDown.imageset/KBDeleteDown~iphone@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBDeleteDown.imageset/KBDeleteDown~iphone@3x.png create mode 100644 Mini vMac/Assets.xcassets/KBDeleteUp.imageset/Contents.json create mode 100644 Mini vMac/Assets.xcassets/KBDeleteUp.imageset/KBDeleteUp~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBDeleteUp.imageset/KBDeleteUp~ipad@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBDeleteUp.imageset/KBDeleteUp~iphone.png create mode 100644 Mini vMac/Assets.xcassets/KBDeleteUp.imageset/KBDeleteUp~iphone@3x.png create mode 100644 Mini vMac/Assets.xcassets/KBForwardDeleteDown.imageset/Contents.json create mode 100644 Mini vMac/Assets.xcassets/KBForwardDeleteDown.imageset/KBForwardDeleteDown~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBForwardDeleteDown.imageset/KBForwardDeleteDown~ipad@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBForwardDeleteDown.imageset/KBForwardDeleteDown~iphone@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBForwardDeleteDown.imageset/KBForwardDeleteDown~iphone@3x.png create mode 100644 Mini vMac/Assets.xcassets/KBForwardDeleteUp.imageset/Contents.json create mode 100644 Mini vMac/Assets.xcassets/KBForwardDeleteUp.imageset/KBForwardDeleteUp~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBForwardDeleteUp.imageset/KBForwardDeleteUp~ipad@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBForwardDeleteUp.imageset/KBForwardDeleteUp~iphone.png create mode 100644 Mini vMac/Assets.xcassets/KBForwardDeleteUp.imageset/KBForwardDeleteUp~iphone@3x.png create mode 100644 Mini vMac/Assets.xcassets/KBHide.imageset/Contents.json create mode 100644 Mini vMac/Assets.xcassets/KBHide.imageset/KBHide~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBHide.imageset/KBHide~ipad@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBHide.imageset/KBHide~iphone@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBHide.imageset/KBHide~iphone@3x.png create mode 100644 Mini vMac/Assets.xcassets/KBKey.imageset/Contents.json create mode 100644 Mini vMac/Assets.xcassets/KBKey.imageset/KBKey.png create mode 100644 Mini vMac/Assets.xcassets/KBKey.imageset/KBKey@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBKey.imageset/KBKey@2x~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBKey.imageset/KBKey@3x.png create mode 100644 Mini vMac/Assets.xcassets/KBKey.imageset/KBKey~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBKeyDark.imageset/Contents.json create mode 100644 Mini vMac/Assets.xcassets/KBKeyDark.imageset/KBKeyDark.png create mode 100644 Mini vMac/Assets.xcassets/KBKeyDark.imageset/KBKeyDark@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBKeyDark.imageset/KBKeyDark@2x~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBKeyDark.imageset/KBKeyDark@3x.png create mode 100644 Mini vMac/Assets.xcassets/KBKeyDark.imageset/KBKeyDark~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBNumPad.imageset/Contents.json create mode 100644 Mini vMac/Assets.xcassets/KBNumPad.imageset/KBNumPad@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBNumPad.imageset/KBNumPad@2x~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBNumPad.imageset/KBNumPad@3x.png create mode 100644 Mini vMac/Assets.xcassets/KBNumPad.imageset/KBNumPad~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBOption.imageset/Contents.json create mode 100644 Mini vMac/Assets.xcassets/KBOption.imageset/KBKeyOption@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBOption.imageset/KBKeyOption@3x.png create mode 100644 Mini vMac/Assets.xcassets/KBOption.imageset/KBOption@2x~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBOption.imageset/KBOption~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBShiftDown.imageset/Contents.json create mode 100644 Mini vMac/Assets.xcassets/KBShiftDown.imageset/KBShiftDown~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBShiftDown.imageset/KBShiftDown~ipad@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBShiftDown.imageset/KBShiftDown~iphone@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBShiftDown.imageset/KBShiftDown~iphone@3x.png create mode 100644 Mini vMac/Assets.xcassets/KBShiftUp.imageset/Contents.json create mode 100644 Mini vMac/Assets.xcassets/KBShiftUp.imageset/KBShiftUp~ipad.png create mode 100644 Mini vMac/Assets.xcassets/KBShiftUp.imageset/KBShiftUp~ipad@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBShiftUp.imageset/KBShiftUp~iphone@2x.png create mode 100644 Mini vMac/Assets.xcassets/KBShiftUp.imageset/KBShiftUp~iphone@3x.png create mode 100644 Mini vMac/KBKey.h create mode 100644 Mini vMac/KBKey.m create mode 100644 Mini vMac/KBKeyboardLayout.h create mode 100644 Mini vMac/KBKeyboardLayout.m create mode 100644 Mini vMac/KBKeyboardView.h create mode 100644 Mini vMac/KBKeyboardView.m create mode 100644 Mini vMac/Keyboard Layouts/British.nfkeyboardlayout create mode 100644 Mini vMac/Keyboard Layouts/Spanish (ISO).nfkeyboardlayout create mode 100644 Mini vMac/Keyboard Layouts/Spanish.nfkeyboardlayout create mode 100644 Mini vMac/Keyboard Layouts/US.nfkeyboardlayout diff --git a/Mini vMac.xcodeproj/project.pbxproj b/Mini vMac.xcodeproj/project.pbxproj index f4e29d7..10d35ca 100644 --- a/Mini vMac.xcodeproj/project.pbxproj +++ b/Mini vMac.xcodeproj/project.pbxproj @@ -9,6 +9,10 @@ /* Begin PBXBuildFile section */ 28848B621CDE97D600B86C45 /* InsertDiskViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 28848B611CDE97D600B86C45 /* InsertDiskViewController.m */; }; 28848B651CDE97E900B86C45 /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 28848B641CDE97E900B86C45 /* SettingsViewController.m */; }; + 28BA897E1CE7315400A98104 /* KBKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 28BA89751CE7315400A98104 /* KBKey.m */; }; + 28BA897F1CE7315400A98104 /* KBKeyboardLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 28BA89771CE7315400A98104 /* KBKeyboardLayout.m */; }; + 28BA89801CE7315400A98104 /* KBKeyboardView.m in Sources */ = {isa = PBXBuildFile; fileRef = 28BA89791CE7315400A98104 /* KBKeyboardView.m */; }; + 28BA89821CE7336500A98104 /* Keyboard Layouts in Resources */ = {isa = PBXBuildFile; fileRef = 28BA89811CE7336500A98104 /* Keyboard Layouts */; }; 28CE8EB51CD4C3B200FE25A8 /* GLOBGLUE.c in Sources */ = {isa = PBXBuildFile; fileRef = 28CE8E931CD4C3B200FE25A8 /* GLOBGLUE.c */; }; 28CE8EB61CD4C3B200FE25A8 /* IWMEMDEV.c in Sources */ = {isa = PBXBuildFile; fileRef = 28CE8E961CD4C3B200FE25A8 /* IWMEMDEV.c */; }; 28CE8EB71CD4C3B200FE25A8 /* KBRDEMDV.c in Sources */ = {isa = PBXBuildFile; fileRef = 28CE8E981CD4C3B200FE25A8 /* KBRDEMDV.c */; }; @@ -40,6 +44,13 @@ 28848B611CDE97D600B86C45 /* InsertDiskViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InsertDiskViewController.m; sourceTree = ""; }; 28848B631CDE97E900B86C45 /* SettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsViewController.h; sourceTree = ""; }; 28848B641CDE97E900B86C45 /* SettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsViewController.m; sourceTree = ""; }; + 28BA89741CE7315400A98104 /* KBKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KBKey.h; sourceTree = ""; }; + 28BA89751CE7315400A98104 /* KBKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KBKey.m; sourceTree = ""; }; + 28BA89761CE7315400A98104 /* KBKeyboardLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KBKeyboardLayout.h; sourceTree = ""; }; + 28BA89771CE7315400A98104 /* KBKeyboardLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KBKeyboardLayout.m; sourceTree = ""; }; + 28BA89781CE7315400A98104 /* KBKeyboardView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KBKeyboardView.h; sourceTree = ""; }; + 28BA89791CE7315400A98104 /* KBKeyboardView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KBKeyboardView.m; sourceTree = ""; }; + 28BA89811CE7336500A98104 /* Keyboard Layouts */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "Keyboard Layouts"; sourceTree = ""; }; 28CE8E881CD4C33E00FE25A8 /* CNFGGLOB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CNFGGLOB.h; sourceTree = ""; }; 28CE8E891CD4C33E00FE25A8 /* CNFGRAPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CNFGRAPI.h; sourceTree = ""; }; 28CE8E8A1CD4C33E00FE25A8 /* EMCONFIG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EMCONFIG.h; sourceTree = ""; }; @@ -109,6 +120,20 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 28BA896E1CE7314500A98104 /* Keyboard */ = { + isa = PBXGroup; + children = ( + 28BA89811CE7336500A98104 /* Keyboard Layouts */, + 28BA89741CE7315400A98104 /* KBKey.h */, + 28BA89751CE7315400A98104 /* KBKey.m */, + 28BA89761CE7315400A98104 /* KBKeyboardLayout.h */, + 28BA89771CE7315400A98104 /* KBKeyboardLayout.m */, + 28BA89781CE7315400A98104 /* KBKeyboardView.h */, + 28BA89791CE7315400A98104 /* KBKeyboardView.m */, + ); + name = Keyboard; + sourceTree = ""; + }; 28CE8E871CD4C33E00FE25A8 /* mnvm_cfg */ = { isa = PBXGroup; children = ( @@ -198,6 +223,7 @@ 28848B641CDE97E900B86C45 /* SettingsViewController.m */, 28F676C91CD15E0B00FC6FA6 /* Main.storyboard */, 28F676CC1CD15E0B00FC6FA6 /* Assets.xcassets */, + 28BA896E1CE7314500A98104 /* Keyboard */, 28F676CE1CD15E0B00FC6FA6 /* LaunchScreen.storyboard */, 28F676D11CD15E0B00FC6FA6 /* Info.plist */, 28CE8E8E1CD4C3B200FE25A8 /* mnvm_core */, @@ -274,6 +300,7 @@ files = ( 28F676D01CD15E0B00FC6FA6 /* LaunchScreen.storyboard in Resources */, 28F676CD1CD15E0B00FC6FA6 /* Assets.xcassets in Resources */, + 28BA89821CE7336500A98104 /* Keyboard Layouts in Resources */, 28F676CB1CD15E0B00FC6FA6 /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -285,6 +312,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 28BA897E1CE7315400A98104 /* KBKey.m in Sources */, 28CE8EC01CD4C3B200FE25A8 /* SCSIEMDV.c in Sources */, 28CE8EC11CD4C3B200FE25A8 /* SNDEMDEV.c in Sources */, 28CE8EB51CD4C3B200FE25A8 /* GLOBGLUE.c in Sources */, @@ -299,12 +327,14 @@ 28848B651CDE97E900B86C45 /* SettingsViewController.m in Sources */, 28CE8EB71CD4C3B200FE25A8 /* KBRDEMDV.c in Sources */, 28CE8EBC1CD4C3B200FE25A8 /* ROMEMDEV.c in Sources */, + 28BA897F1CE7315400A98104 /* KBKeyboardLayout.m in Sources */, 28CE8EBB1CD4C3B200FE25A8 /* PROGMAIN.c in Sources */, 28848B621CDE97D600B86C45 /* InsertDiskViewController.m in Sources */, 28F676C81CD15E0B00FC6FA6 /* ViewController.m in Sources */, 28D5A3FD1CD6868F001A33F6 /* TouchScreen.m in Sources */, 28CE8EC21CD4C3B200FE25A8 /* SONYEMDV.c in Sources */, 28F676C51CD15E0B00FC6FA6 /* AppDelegate.m in Sources */, + 28BA89801CE7315400A98104 /* KBKeyboardView.m in Sources */, 28CE8EBE1CD4C3B200FE25A8 /* SCCEMDEV.c in Sources */, 28CE8ECC1CD4CDC500FE25A8 /* MYOSGLUE.m in Sources */, 28F676C21CD15E0B00FC6FA6 /* main.m in Sources */, diff --git a/Mini vMac/AppDelegate.h b/Mini vMac/AppDelegate.h index fd7103b..2f627d2 100644 --- a/Mini vMac/AppDelegate.h +++ b/Mini vMac/AppDelegate.h @@ -38,6 +38,9 @@ typedef enum : NSUInteger { - (void)moveMouseX:(NSInteger)x Y:(NSInteger)y; - (void)setMouseButton:(BOOL)down; +- (void)keyDown:(int)scancode; +- (void)keyUp:(int)scancode; + - (BOOL)insertDisk:(NSString*)path; - (BOOL)isDiskInserted:(NSString*)path; diff --git a/Mini vMac/AppDelegate.m b/Mini vMac/AppDelegate.m index 6bf6306..9a784ba 100644 --- a/Mini vMac/AppDelegate.m +++ b/Mini vMac/AppDelegate.m @@ -22,6 +22,7 @@ IMPORTPROC SetMouseDelta(ui4r dh, ui4r dv); IMPORTFUNC blnr Sony_Insert1(NSString *filePath, blnr silentfail); IMPORTFUNC blnr Sony_IsInserted(NSString *filePath); EXPORTVAR(ui3b,SpeedValue); +IMPORTPROC SetKeyState(int key, blnr down); static AppDelegate *sharedAppDelegate = nil; NSString * const MNVMDidInsertDiskNotification = @"MNVMDidInsertDisk"; @@ -40,10 +41,18 @@ NSString * const MNVMDidEjectDiskNotification = @"MNVMDidEjectDisk"; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { sharedAppDelegate = self; - // TODO: setup settings + // default settings + NSString *defaultKeyboardLayout = @"US.nfkeyboardlayout"; + NSLocale *locale = [NSLocale currentLocale]; + if ([[locale objectForKey:NSLocaleCountryCode] isEqualToString:@"GB"]) { + defaultKeyboardLayout = @"British.nfkeyboardlayout"; + } else if ([[locale objectForKey:NSLocaleLanguageCode] isEqualToString:@"es"]) { + defaultKeyboardLayout = @"Spanish.nfkeyboardlayout"; + } NSDictionary *defaults = @{@"speedValue": @(WantInitSpeedValue), @"trackpad": @([UIDevice currentDevice].userInterfaceIdiom != UIUserInterfaceIdiomPad), - @"frameskip": @(0) + @"frameskip": @(0), + @"keyboardLayout": defaultKeyboardLayout }; [[NSUserDefaults standardUserDefaults] registerDefaults:defaults]; @@ -206,6 +215,27 @@ NSString * const MNVMDidEjectDiskNotification = @"MNVMDidEjectDisk"; SetMouseButton(down); } +#pragma mark - Keyboard + +- (int)translateScanCode:(int)scancode { + switch (scancode) { + case 54: return 59; // left control + case 59: return 70; // arrow left + case 60: return 66; // arrow right + case 61: return 72; // arrow down + case 62: return 77; // arrow up + default: return scancode; + } +} + +- (void)keyDown:(int)scancode { + SetKeyState([self translateScanCode:scancode], true); +} + +- (void)keyUp:(int)scancode { + SetKeyState([self translateScanCode:scancode], false); +} + #pragma mark - Disk Drive - (BOOL)insertDisk:(NSString *)path { diff --git a/Mini vMac/Assets.xcassets/KBCapsLock.imageset/Contents.json b/Mini vMac/Assets.xcassets/KBCapsLock.imageset/Contents.json new file mode 100644 index 0000000..cbe56fb --- /dev/null +++ b/Mini vMac/Assets.xcassets/KBCapsLock.imageset/Contents.json @@ -0,0 +1,32 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "1x" + }, + { + "idiom" : "iphone", + "filename" : "KBCapsLock~iphone@2x.png", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "filename" : "KBCapsLock~iphone@3x.png", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "filename" : "KBCapsLock~ipad.png", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "filename" : "KBCapsLock~ipad@2x.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Mini vMac/Assets.xcassets/KBCapsLock.imageset/KBCapsLock~ipad.png b/Mini vMac/Assets.xcassets/KBCapsLock.imageset/KBCapsLock~ipad.png new file mode 100644 index 0000000000000000000000000000000000000000..b5bb1b32750f7ccc6dc5e30c06b11c5af4459bb2 GIT binary patch literal 236 zcmVP000~a1ONa4J}ul^00029NklB{6TU{A77}iet>Z}Tz+LY{{Rq@}t z7A5*?MO<0cq@X{Rg_~B@$?3Nxk+ABojDA@Zsh0gF*$)dM)1sg5^v%4;HSdQTeK98r z&HD=Q$*d@4&S&^wT9h{9BLkw6erh&FX*+7%EHWi}kCWr-lL?V(L1rCBMZ%a)Z?9ha mB4JI#}f?6>!fiE!6D;U?K3Zs*4sS P00000NkvXXu0mjf2k(8^ literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBCapsLock.imageset/KBCapsLock~iphone@2x.png b/Mini vMac/Assets.xcassets/KBCapsLock.imageset/KBCapsLock~iphone@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2ffc046392c101818ce17200f8e23f603ccc671b GIT binary patch literal 302 zcmV+}0nz@6P)NklL0uH!xzhs?XlOS=50|EhzkN~-axM&9yk#D|)Q zkR?mRo@*vbRz`#-ENUS}_S>S~xi;d0{NS4LP{$ui7IV*a3FW}GppPC$N*331eL@+y zHWc-fFjB&`V`zY+iW07a5)F}3Q^IxP(1=S)xGw%_%mXD{H@}+jND0@&x27y8;d()u zv806S1E}|b60QnX$ojNps3qbH^+*-}mQ~DLh=rIfmOz>%07*qoM6N<$f|m$~ ANB{r; literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBCapsLock.imageset/KBCapsLock~iphone@3x.png b/Mini vMac/Assets.xcassets/KBCapsLock.imageset/KBCapsLock~iphone@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..1dfefcfc8c75464ececc0e5d4f5e2731c9cc2f7a GIT binary patch literal 366 zcmV-!0g?WRP)hlUKOhlW_S1PvO*V_^g8p<&h#L=TM^P!El;jv#tylnn$?Kpj9Q zG{z=^D4-6Y6B=U+LG;l0JA-B`1~uR|g6N?UHPE2x(1@t64hEIU5y(*{i{KA^YTOm= zB=JgOHH$PzpY%ze^huvQwE)j>YR!V&z^Ob7vJj{KvEUJ0CJkKCA~xT2PfyvTm;`=Q zetwCRvyZ%aI~4E-r}`1kYu5jh{Qq*~hy~e=QyDaH M07*qoM6N<$f+1az_y7O^ literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBClearDown.imageset/Contents.json b/Mini vMac/Assets.xcassets/KBClearDown.imageset/Contents.json new file mode 100644 index 0000000..07d949d --- /dev/null +++ b/Mini vMac/Assets.xcassets/KBClearDown.imageset/Contents.json @@ -0,0 +1,32 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "1x" + }, + { + "idiom" : "iphone", + "filename" : "KBClearDown~iphone@2x.png", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "filename" : "KBClearDown~iphone@3x.png", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "filename" : "KBClearDown~ipad.png", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "filename" : "KBClearDown~ipad@2x.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Mini vMac/Assets.xcassets/KBClearDown.imageset/KBClearDown~ipad.png b/Mini vMac/Assets.xcassets/KBClearDown.imageset/KBClearDown~ipad.png new file mode 100644 index 0000000000000000000000000000000000000000..3ca10ed605420e67f0d4cac68db2e746463a49cc GIT binary patch literal 258 zcmV+d0sa1oP)0^S$!H$g4<{BkbEW_YOJBo+^v(amXA(z^E|!Mx6y7g;@Cg6M+8 ztiC*E^n=#P4bd@Z5(d0B39pL5h4imaE(+pbbAuJ>559t%x{z&RRrV#H#Bh4dIO1JRR91007*qo IM6N<$f+#y@X8-^I literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBClearDown.imageset/KBClearDown~ipad@2x.png b/Mini vMac/Assets.xcassets/KBClearDown.imageset/KBClearDown~ipad@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d35aecbae14edea563d0746c5c3cc329c713d6f2 GIT binary patch literal 401 zcmV;C0dD?@P)7{=iz=w)QQDC>Aza%J)p^6?Y0=S^bEmVn} zJnQYi$f-XNefDP54wOV~K9Q=4GT#oAL}fmaDv2^b0FV=9z8xruGCv&9=YW1Vpv>11 zmHAUnRT7o?kx?a4nNLzBQJt?P>hmSh4y>2?lCa(mER^|@u+R>Cwq&i$XT+K%pDTcX z`4O7`!Rt`qHTOe-`*fi|$Kg=mFw(lQ6EX> z0OnF#WUBy$ZChlc08rS}MOMyM8|Y9PdD>{Tcxt2segF&4s!B6zbjtt$ N002ovPDHLkV1lQ1hD-ne literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBClearDown.imageset/KBClearDown~iphone@3x.png b/Mini vMac/Assets.xcassets/KBClearDown.imageset/KBClearDown~iphone@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..2093155c58ee7f495e7e631b0881adfabf013ff9 GIT binary patch literal 451 zcmV;!0X+VRP)B^=Yl8q4KE%v?_-@cZVy^*{4wy;Ucfjn!l>_Dq?j11qAlBZ0Pmr+JvNM}xzc-Iq znB4_I>az4NQ)Ro0kx_T!zdsOqGxoF3Q;iTdt$yvid4u8%)C%RY9vV~Nj0QKmJyZ} zkb;?QA}iiGD@|xt$tVy#q(u~vf|>C_{GgdI%&Lc^Jj4%5dC+QbrtNRU0<{Ha4k=S$ z5wW&Nfij1B#`Kv_$bb99XT+MTzk`nc+&cQJ?r7oRXc<*?uqdno48_b0IbE7x literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBClearUp.imageset/Contents.json b/Mini vMac/Assets.xcassets/KBClearUp.imageset/Contents.json new file mode 100644 index 0000000..83c2f96 --- /dev/null +++ b/Mini vMac/Assets.xcassets/KBClearUp.imageset/Contents.json @@ -0,0 +1,32 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "1x" + }, + { + "idiom" : "iphone", + "filename" : "KBClearUp~iphone.png", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "filename" : "KBClearUp~iphone@3x.png", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "filename" : "KBClearUp~ipad.png", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "filename" : "KBClearUp~ipad@2x.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Mini vMac/Assets.xcassets/KBClearUp.imageset/KBClearUp~ipad.png b/Mini vMac/Assets.xcassets/KBClearUp.imageset/KBClearUp~ipad.png new file mode 100644 index 0000000000000000000000000000000000000000..e4299d8f37e61c52bbf00b911cf6205fbe79f2e0 GIT binary patch literal 345 zcmV-f0jBi-zZp>@!pt0GUh$M+!+Yn@r&4k_6jWz zw6_JtROw9G2xX<^+RRoD~JXz9V8R7e^nScRm~ ztppk`q45N|RYqr%uQOElYJlY}(*b1!FR5y|!48%% zfuY3BP$sa19o(R*50H2fMA^WI084+6c^@j8%axi_GuIXz;Y>a_0AO^-k zSP(noAS{TTaS#^7&KL*-VqrvtK>9Qz^a+485lxI=q)*p3zK>6&nds~K6)+%X0&p+} z#7sCC17arZj2+qMAjJ6ksXyl=#Q6Es8qFE@Bf`P#=M1BbNPsjUMI8nZA_LT2gx?y* z5F%sL{Tn`NSU`v@Q1?f;rePByvPs?7;E0AKA(B-05m=@n!)$zKHW_s<19+_Q5rgoV zK|E6LW0d8Tbo!9l9-qZSb(UBTD7&a?$r7=S#Z00fang@TEM^@MS*lug(Ue1aHhz~9 zhiJhw1@7)U++~-n&}DJ}EWiPfqeRx&5zr&xJgUCxuX+({y5^sexB87_ zW?oBm9P`RMU;P#Zf4_b8&MU`s$gMXcD*tAzms;zisQ>fPTIiS(!VeE@v{Xk)9ZPNW zzz-oz>2M@5`_!~AQAc>u4A0c|Ob9P{(+oS+wo?di_}UCB)wWUyU-{Jx1GNo=@QY~v za?o+v33GMAWgR(^FgJ8g2)Ac3w}o)79NH&lQ;7-t$|3Hy)GX#M#n5bOHd78|>XuuX zsem$-nOcZ@E#n^b5F`Ey;jfW;h|$C}l+ijUGvmfvss_poW>JMuX2!IQWvU$7Hj9xd z+sdI+?TgCg7wA+u)OEudb9KfI-MOHl{jd%3r}j-VY*pLV^y8n_(POne)~utvAor_j ze_oIs)6Mujt?a_m%Kk2dX`LY+B8GUZ$p@|X|8>xMpATC612wI{Ax9z*3)Mff-*}8QyVZd)BBy!O$2Y=95LO$ zG$83-eoKysQ7U|3iVe0~I&3h-2P%vbk;6=uVLr2+=;1TNWcedUsInd8P~`}q9OpIL z!49uEj!TA`?p`3KoAaEeKx-**n)7rMv!JRqG5|2ByDFR^xtyV*%7XwozG$fyNj61o z0bj^Lq(ygzL# zv!F$;`BCt|z%1wicv8P%z5JeD6Y`+oA;L`PA^1}^W<%bTjoFYFWn+`|As$0d3q|#@@!S8{zCe_qMV_-LEC3~l`@nsKB~A4Ux(5K<6V~rR)q@PopRyqv z^QQ(3$ifDEsk;tZaFB&9IP9(;HDN+FHsMEI$R3e&an5r@a*i%zM3UOz zM>#?*5LDxcHKhBJF!RM4(m{r6JmCc|`NR~W1L6iP+$j!!Dk`hyTn)k O0000csaKU$=Ov@nsVOhR`<#v8WT=Bmy1|O_f8P8i)O7N;yM-G znO~=PL{Ej4L|mnw!Mq}VGr4Ar>?`rPk=1jQh@BL!7oQtZeKYD5duztpB9>F%pk5Zw zDZD4nSMDWowPqYGeo#?nf6-Kxy=HQCRIKAI z>5u!wb_P+-G`4ZLvz=G9`$b&kyoQT-y5@W5I?9e_Q&H~EdJ&at1+)TM0j+>mKr5gX z&W<*8nOG6LGu(lln!xVMzLSKykLa%&Ty&=r9|9N&w^gQQWKWytsisEr1im zcL0NE6Z`zB0qcku513BGl7DKzlp>Y`78Eh7a+`@5USr>j!;~9U#D>5qA~sd-4Dq36 zJSgr^Zc!1l0DL0OQ0^IVqh_2azE*Az(P40aTf}>YP~U{w#DSWzp@@0Z*TZAt6@^Vi zoT8qd9ub{p(Ts8YEZ#PhdUg?Uh(g6ZA{Hw-VR4jZZ6zfabsE2mR}H4XczzH++DNTI z9Hz7QK)KojI>*pzE$eg9VHP#k)g|Ia;{m9c*>fU(HjCado-ahaYau-WjN)vOdau0| z{VXCrb(0g_<2w=Yks0)^@x3g);vaUhYrHQaI&7_uNnI~BYj?7d^ogMy=x3?fgJxE0 zTKl`-TfXwD+ih=LePa}xy1`4n^sYx7X-kdg0006JNklX*?RBAC>07PzQPUev@c#x*O;cS7HyTb0-Rc9s_${gYVyx+s;z;>9 z?MY0E{pw?Ph`B^T8|<{snv`Fr7Y-#X$`fMO1->3NTM&tM0= zdy@a+E@OP`5zngVpeiDMv&eos?KQ&7PUdL??XlB-%Vlw~n5#YSZBM$$sXgv(FSrgM Y5lV^69-{XX=>Px#07*qoM6N<$g74QHQ2+n{ literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBCommand.imageset/KBKeyCommand@2x.png b/Mini vMac/Assets.xcassets/KBCommand.imageset/KBKeyCommand@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8480dcdc1b3ea491fc3d8d5a057ce849792ed8f2 GIT binary patch literal 683 zcmV;c0#yBpP)PAB7(!>BSi8 zkw`{FQ-cuP~AC~b_C)|9*(FvfeSWk^vHEe;DHadd09;lgai6N z`AFiRMsi@^qVL7t!4s4i-WT@};SFh)WAcIyYl%rowN)`0Chj|YAzTBUu=!BzOiX%e zHW-tNYKwq3-^y|@HqZ2YvCl!urjgp`0q)B3EH1xt*d}qG-~zB*BV&Hmu(s-sAVl%5 zij(xF2GynB5_3R1Db{LEdpfg6%~#|?X`<@a*KiD4@WHPh^d$pu!6J^0JjwE)*Vs!g z(jb_8R2% zBym~30l{onw-n)=G_Ml;tL~-;H}sVUT=-O)Nx%Bh=%_XlVszOS3;k*=!eo$UJuxY( zwj{XdrJjsQhz5FMQbTP4OggEJNV;aPS=Vf$28v@6rFIiZxN}o~FAUYlY2pEJp*P=Z zz#E(Enw^3N071^(T&}aZrs6YBvV+Ik{XkW0AU9tbAMAs?uidKzq2$!J&aT;{Mjw7i zk^G9v7{qbdI{ZG(NsCInY`>f2A_euC$uVxQj($Xw9)Wac1y?x30$Ssa@)HqTV^$xI RZF2ws002ovPDHLkV1iWx000A=NklDOSn4GB1#Re{P-vWP_% zLlLa2oJqP9r2zX0r*IwBKqg1f95FsiES^P-%{hWhpqgu#WWNRl)bfHU_n;Wd6|B^3 zKG}F=Ghd%s$rV@%>58Z?QcFMc4NC~}BBEY|$3@NJjg60!OrGU(up9Qv-yxV{LCNf82$4_TC*TW)W;z2)|n@yX_5zN8C1>8DQAw_Kt%>k`c;nrctF@)ehm zLj;*T$vh;%&gWUOaM_uG$VPGqyAfhPrXw5C85{tX5=J8%*|C&iXoC$$(~w8GU2Uw% zIIUU7Y2MgWAEp7L5Mw)(qfL1wuaK6oO_IthtVGmfsHLeXUX$>Bc@3gA1AtWKXsP8! zISN{8K8I9*y%F{3jndLi+&%y&nO`xoH%iNF^Xtk<0O#n-1^7I%*8kEzfzJYc;v9g( z5Or4^%<}4hM+eQD;;{>&9tKd#8ZF#Lm~^x6KEvZPvv1Q0bF1cSCJL<6xtZ0P%_R$uEasXecng+tzVm(`%f%k)v$%sjNFt9D zXpb15fanFLGkzlZB(WQ}G6Q?G=b_wy1YFFTKxH7A#3Cbf@vUJI67XiQe1Jqcot=qd z=eU>OCj?05eI%x1*pGPZ$C${5DL7n@|4Ca>!rD+mEBx#EW@3`dQ}i^mIFm*k$+~bP zjhJa>v=`5iM_h_Im@_z*-AQJBNM?7Av@T1UpYUMhe&=bZAV)l`j3qw+S74ui@JS|Mmm(9)IyI0Y4&%8iGG~18`sq z@+JJE-9RnD7v2c88dnIosQAL$2uwk)`rW&LIvRj>3lCvbKqQ5XhcKby0Sy4u$%1^2 z6}%p3r&Cq{6mzQJ9Ut4W0)P_EH2k9-K)s>(N-5{!kCaU3zat$aOFt4onW(t3j$9k} z$X~?;p<}Q3mvSy01tS=#a8y)*tvE~=??p^DJ*@H)2lTn1pGtrTHV_<{=D2O&00000 LNkvXXu0mjf*`a-> literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBDeleteDown.imageset/KBDeleteDown~ipad@2x.png b/Mini vMac/Assets.xcassets/KBDeleteDown.imageset/KBDeleteDown~ipad@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4ee72f69212748770c6090ba0811d0071b8a6ccf GIT binary patch literal 526 zcmV+p0`dKcP)djBRvb6}yRT>%NmNkLS(VbMdd;e`{S|1R64z zZQSJ>rdIpHJMMCVb@WE2K1Dpo1h&JpKwtnjF#+!x18`&vUoj!<6&-M3oT?Xgh8mfm z>!pn%E0c7+xN5L7nQxeocMdC4_>KvE53w+vpO^^tfsD)yUathqj!bX(N*Z$o{d>x& zB8IUnMiphe$6#Ly^QEIN0U(!{MV|vev3tM*eqn@FLOj8uhf3Fgh5W`y^H>{Cu;_E} z+yWN!N0QqU1bsYUXU2tF+tNCfN}D3Z%@b(^#(feL08%nM7@Ely3U>; z>kW7T1;mm9V(tM40@dpYbm2{a`W$k^1Yj^<1JiqWVgdl8`RZ367m5i0z$iVzB2yEH zCwvJ=uO$#?q?}NRUE=AVV;IS2DWEv(F?#wB35?*A5b&N-Dl+Kf2^ExTzhp3ikC+IT zfx&!;O#g+942mq_ITi*+lyC_v19*=KeH-BaO;|`)`iGqGj0R9s!rKr73UHt=uS3%B zOKW{Qv*} literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBDeleteDown.imageset/KBDeleteDown~iphone@2x.png b/Mini vMac/Assets.xcassets/KBDeleteDown.imageset/KBDeleteDown~iphone@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d0e7eb93c1676ccd14e214ec49ad13f95b973aab GIT binary patch literal 374 zcmV-+0g3*JP)z?~4 z8hP!WGbS3LG2^`voq1x6+RPL^^2s=rnJ4<-sgZK9K=jFEITne&IWJ|2=pPUKmt|rg zyzy5G>qG_L9dce|oU-3{k@!oyK~!=Wg{^7(DsZG)T-YRPxBv=UvKCiFBArmh>~!QW z?Fx~B!ZsngsFOB>O`;`peydkEXoIOjbXF&A!b{PKxd4`nL5f!c3Y+W2vpQ?jPqhCp zZw+9%d5vlmc68K+XjE(aOJ0-4)cjhtcK^Loe`}NaPW=$tC@R^XwST>k6C%-aY<_dt z6(MuXF5l|Kuwrn7emHB$19BFNzBxa%<$q{}W!M#K_@yw{*lQkn-?GM@^}ELY03DMa UiF;)UuK)l507*qoM6N<$f>^SsVgLXD literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBDeleteDown.imageset/KBDeleteDown~iphone@3x.png b/Mini vMac/Assets.xcassets/KBDeleteDown.imageset/KBDeleteDown~iphone@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..2e2275522c2eeef08ca011bf782d02480755d1ee GIT binary patch literal 560 zcmV-00?+-4P)rU|)HliA!wf%=?B%irKsQk_y||4E=@}}dH+N7$y+vyJ zau*fWXQX5h_mNlM@&-Bk`g4`pe=@}T;&hy*G^Pq=Hlgw79?wf}IEjWMC!;UZLO&8zCqnp%ws8lkEr< zzd5RY5&&vA81^00K&a0=>YEwBaGnMu>_r?fGXNON^I(K8J@}Kc4WV$SZ8u>LLQaO` z0`@c)p>_(v%J?m#c@lz literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBDeleteUp.imageset/KBDeleteUp~ipad@2x.png b/Mini vMac/Assets.xcassets/KBDeleteUp.imageset/KBDeleteUp~ipad@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ab4c7efc308d50875b3cc2fcd2fdc838568aa36d GIT binary patch literal 726 zcmV;{0xA88P)D{a5cU&`zx9>pMkNYqrVtJgga7R}YtN>@ zW&}!rUS{=uIzlYLRLiV?4{Otea4;|e6fqNC6h=4{AOVuhj5p-lR3RL;5rRid37#ND znK@tVgrh1#GAkHI2)05P#|o0MB(uobBu4~TWiGve5DQ_WIe)bhk%MqdNr3TWSQtVu zf8+ItiE`3$H334|#2a&M#&Y!UL6n1UErNhQcq%#>G1 zwg=@3DFjyyGvhwi`U~R{Nd#4A`Q-z91LSS~VqlETX8nZ}*g*O0c_9!+c|Mud8&ODu z_!ZED62)n=@);Bj5ylOw`+6^&GOPZeF_P}y_{t8tP>14#ufHM@Pc^3J7SO?|U7 z(qcXI0O4_|o0O{Nh+CCOF(lJjtTLT_Bj@PM4>+Xs5$NS6EdZ%s_08%cB` zbYviB`DyANIfaucsrS=3Vx!g|-qLO+C7WPZMx8*-Ws4Mlu^o`3+zgVg(!kOhR*S6d!^BD(7=CFdOCJ*JX5Tg&_ zm@AFm{{cNz9?-J2*o`+X*w!M1rf7VrDN@_HdrYPq3DB|a+z(Utm`I3?XxJ^ZLVwQl jf)D(tv-y8p+5G1LtOnLyZ-N&b00000NkvXXu0mjf_crYv literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBDeleteUp.imageset/KBDeleteUp~iphone@3x.png b/Mini vMac/Assets.xcassets/KBDeleteUp.imageset/KBDeleteUp~iphone@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..85ede96ecc3b8ecd9cb838a218cce70143bdab74 GIT binary patch literal 668 zcmV;N0%QG&P)*_P3lnVFfHnYaGbl))8ULABny2X?(FGt)9N&6zVIdAT>u>->vq=1U&m zpll$=cuED|sHO}3S38xwCCweyQAEs!@rXvc68rkVc?watoS$?h{*_@6sy5L=S6;qe zFcfiH9mZQOvx4yqAxWyL3TQfqeS(p{Q!Jx_IdX{0IE1VfxhM`}8Au^=hLp0)Wk?}vhSPFGX=7I)V>w76+i8(A zL=-8*g|I@-5K_bp7cz-z%Q!;(mRZUJmm!Cw?c1JX00~gKT!tLdWd1o1kQl`>^pJ9N z<$jfFW49KPN$jREPXXquT}aX2S%r)SF3=#S=pVjP#tLAmOOZohyZm`ozyMfcDSI>! z*cD9?FjfL6DHak$IjCZ^xC}MLLgW-NBY?731JP3!Ao}<5g%lB8F-0JUc>RFoVGbbp$mxONdWJh~Fh6gxPP_hFug@#ZLJym>0l!1oQ8Sk8C`#~%1jVY__b>vsh1;i5p6L8u$X z<3ul$d{kd1aT>%4o>9R!YGZBQK_zd=a*y>C;nWRk)RGiNf8@9T00005^o?B zc|r0%MdL7+A^uDwF=@#VZ^FoU+W57?0h*cQ31<@YkA21{#fS$2iyWC`_+_pD0000< KMNUMnLSTX!S%ygf literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBForwardDeleteDown.imageset/KBForwardDeleteDown~ipad@2x.png b/Mini vMac/Assets.xcassets/KBForwardDeleteDown.imageset/KBForwardDeleteDown~ipad@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b3de016f0c95ef55ad18e95cd8288b685ff28bcc GIT binary patch literal 526 zcmV+p0`dKcP)_s@twa&qgKr`_OpyusOiWVB(Qy;fEFki$$KQ=Jz^vt>B(m#gjF#WPh3SpS{2hV zG8qYRRm{XdB@%MNY+SjHguH6z;KC~;^wlsIM}8s^td0fxfS?l=k&!ASvV0d)HKHNF$7m;!)G?|!XL zKN$ef>jOym%Vh1(vE>q1uGUY|{vokk-=9L2{tuHhf60m|b9swx$|&ZCLjRkIB!fh_ zU+H@I8=3wmW5~dV$n>Mh$nA*qqi|qn1o|J0#F171R}U?DAF_S~2J%AGe>3Z$35P<` ze_n1#{I5aK)qM_agZmKr zMkk|)fn|pH<~lahIOxmMxtdDTxlD^vy8hEGSkf=C(k}{sm$5Se%h+2ES@o5%KLCdF V9*O8dc98%8002ovPDHLkV1j5HrWODI literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBForwardDeleteDown.imageset/KBForwardDeleteDown~iphone@3x.png b/Mini vMac/Assets.xcassets/KBForwardDeleteDown.imageset/KBForwardDeleteDown~iphone@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e282e07f648b704564a99e52a0792a097b380eab GIT binary patch literal 557 zcmV+|0@D47P)Nkl3zH37xUNNTb)vr zR?K8G=Xk(t#9m%-hm)*lDvd~$7A)s2l2ISCkop)in43t(Jx6B@8N*A&LvPR(Wm<9@ z@zh)NK$SI!$KIhgiRr*6#B1-;hlI>Qy!Rgch|Lbfe|bQEVsZ(oNgpzRs(FCasE-&( zmApY}*2fH{VkEX7`G_R;6Q7lgr;M;7&d`#UL`ZCNw4^m>)epH*HpBt|>a!QA+K2`K z%tMrY$ruabJWU-Vp3s!Dh=DOqj}VT=G8}=!h^a4`phpxkB0WLTS4`3)gd-Ih5%vEP zjv%S}hRF_LW#|cdhS(W;g7!)gGh?139L?k|+6%KXctX(1d#IGh>dHDf`C*y9Mt5hOFlTP6cwCAc2aGxWD@ z8=;WVFcssq{sFeTn2;Ic6%#>+IPsUz%n_<3Wp@KaC%@!_@{#V0YPO*{cG{TS{z~gi-Th%Bh00000NkvXXu0mjf2MYZ$ literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBForwardDeleteUp.imageset/Contents.json b/Mini vMac/Assets.xcassets/KBForwardDeleteUp.imageset/Contents.json new file mode 100644 index 0000000..9b6aa14 --- /dev/null +++ b/Mini vMac/Assets.xcassets/KBForwardDeleteUp.imageset/Contents.json @@ -0,0 +1,32 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "1x" + }, + { + "idiom" : "iphone", + "filename" : "KBForwardDeleteUp~iphone.png", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "filename" : "KBForwardDeleteUp~iphone@3x.png", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "filename" : "KBForwardDeleteUp~ipad.png", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "filename" : "KBForwardDeleteUp~ipad@2x.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Mini vMac/Assets.xcassets/KBForwardDeleteUp.imageset/KBForwardDeleteUp~ipad.png b/Mini vMac/Assets.xcassets/KBForwardDeleteUp.imageset/KBForwardDeleteUp~ipad.png new file mode 100644 index 0000000000000000000000000000000000000000..5227be6cbcd4952b79276c3c1d793fb7e1746519 GIT binary patch literal 433 zcmV;i0Z#sjP)dm?u(^Y$lV4`^wMO5Dbk( zW(b*A9QV2(Z6N3oO^h{OFg-W(yc4c^ITgtyU|eqI8Asjr8QDl;x?mjSXFTDcJH7^j zfdF_(MJfSfRd_+xWA^%4mH5xb+!MxB3@Rhfh3@!U0y~8am=eqwPt-_02#m2?7!%O;LJg!sfQ&tkjNlQ%*ei_j{Id^gGZy|6 zV;p}cTe{L%#0VnL6~PD=k!Hl$k1%WzOV#dbA~b|mYJUk>F~-;-Mv|>z2tlopV8}Rt zFl-Q!tp3T+5Y`Y0#*BkZvQXJ))gB>#BYcQyw_BIC>SGz z;3K1`+!NabVL@2UYF`)yxuLG|nwo;JAVd=B1EYZa()mbZ5QI5F{^E_1pKI#$C)$7> zp(p76YF<9BsFR;*3mSw0!3!fFm*j1#z5Pddqyumyre$$a2pw&|)CfV#Sc+MhTo6KM zyRUbP8HFNf7^7HBn&AT>1iFGb!akD+qC0;kKZipETc+k-ke;c{bb>5{^G*b~t+p!> zT$WJu4oEUjdpC}qxrocF;?ykf7b`P zVO1ke`ApbLbzD@R`RwNb&prI>XWU~CGpUJjIU@U}3+XKJGynhq07*qoM6N<$g6XV9 ADF6Tf literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBForwardDeleteUp.imageset/KBForwardDeleteUp~iphone.png b/Mini vMac/Assets.xcassets/KBForwardDeleteUp.imageset/KBForwardDeleteUp~iphone.png new file mode 100644 index 0000000000000000000000000000000000000000..3c93b8085cb3ad05394a92bd32d96a28648624a3 GIT binary patch literal 492 zcmV1u~#F-gmh-cg3Y%`c`+cAb{i-+Ig|BulWJM&`C+eOp#EcB)l{qM^h>Lt-hHQg3sntcPhqB zPLr&hU*Z1t6=pT|4az*b|TaMXY_epBHV9iLEA(lg!*6otm?M^oM1wvo~4@Fe{e-v#XKZ1frUi%Be#y1MZK;$ zJ8F4B53j)moeql@m#B=rYP4{nXktA=QG`e4hwnDdl|q6ZOyz+d=jt9r>l~qj@c*X7 i@c*6nJm(?E zSXzObBGzz=G_R?ol@5=p<_i@(<_P`qOd;oZM@M8;&jW^_&HyrWL|)%niYkM7Mn^2w z#0EmAfUKP{$0N2enGuxWlR=DS85eo&P&N}X$L&mMMiNa*xJJQ0Mn$097 z5Hm%%m^(}brHnG;P*nF;;f5wK z0G7)XnZXVaAOgdsG^r^2|HqIay8{_IiolQ{3(agf%Lt~-^JmDAy8^?d+)%tKwy*;< zkg5EnQhdU$q>3L*mS3J-h7JNn6eFjILjQO8nomc+M6TdYj5fszFV2(-S8dASe9~uS3<)#=@z)00b~>ZDd7Ajc^~O- yl7|ezGexZB4q0APNt@T|YxzP2PdLT^ROtjx?~)XMNFbL00000AAK?10so0~r8!S{m$e9q#eud)nOH{~xIfQL(s? za%n*23_BrIRuCX$QGwZXuk8HGru&!Qhj+87URhbesUej!=8S1)qGEAuT1N+m}wp;OU sJ$8h(EW)l{6_F?jp(Ig36IuMo6J>&oFEHQLQUCw|07*qoM6N<$g2^>vMF0Q* literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBHide.imageset/KBHide~ipad@2x.png b/Mini vMac/Assets.xcassets/KBHide.imageset/KBHide~ipad@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..faa6196dd6c9c4abfb56b023eb3d4b5571eb95ce GIT binary patch literal 438 zcmV;n0ZIOeP)lJT173m3wUa)@7r^F9Ps|AOLy|eE6-*evm?T*j9 zJGwKo5XK{qIGX|T*j(=<)fuA8R06F1qRezWpYJBv7<+YrX1;QSvhvx;FD zMg)&XY+_J0@(_;*W9Afs@P#Y*=ocTC`GO#vXUrT#G%r|7Kdj{$qB+FaIYLp3edGYW zvY!t+SCJ!3gcPr|TUz26x3sn9np(n^ZM%L(eOoi$&wG8(!|5^P$|BE1 zmclc0fafBM;TaG@wuC6%PC|%uYNgvr>2G{x<0~8gmymoe`JADz3YA>O=o(XTP^lQ`H6l$#2 zLUMXLEQI~K`Ce=5Gzk}tmx5J33J=Tzz*y&nCL6Vpk`c}dpKa3)Eo9UPcg&QkR@P_` yE}QSD5ccXU3nscMgz(ZrS<~KT9~{!xfA|VJK_$O#B1Lup0000QPO{ap6%H_N_w_u`zV?Il>FB{+p}$D`zUE3yBs!@(_UVF+RUdK z#R#^dRAoMb@DdIn=B5MCotUxcINbp{vYQ7)3pb z>&74myQxF+O0b*+!bWo4a^%zWgux`IIal=AbL6^$OysS8emxcVKZTe^M4wtoS@J{^ zE@{GDy7F%!j_Cc5=}#Ui&SK&SLW1R#;BSWVLZf$3lRVdnJDPKa7W`gSw(32v8AAaA zp*(9eD#A1h@w1-X*XyULN1zPkiKZPPgaXPC*7GrDQG~#$%`Odm$xr~GHP`jrHCho+ z1sKm8&D=_eImGlROc?^LK4+01ru+27fTGMLhUm17YM9Z6%LtDchA{=GM-j5(J+eVV UzgX0L=l}o!07*qoM6N<$f>xE^$N&HU literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBKey.imageset/Contents.json b/Mini vMac/Assets.xcassets/KBKey.imageset/Contents.json new file mode 100644 index 0000000..db12060 --- /dev/null +++ b/Mini vMac/Assets.xcassets/KBKey.imageset/Contents.json @@ -0,0 +1,103 @@ +{ + "images" : [ + { + "resizing" : { + "mode" : "9-part", + "center" : { + "mode" : "tile", + "width" : 1, + "height" : 1 + }, + "cap-insets" : { + "bottom" : 8, + "top" : 8, + "right" : 8, + "left" : 8 + } + }, + "idiom" : "iphone", + "filename" : "KBKey.png", + "scale" : "1x" + }, + { + "resizing" : { + "mode" : "9-part", + "center" : { + "mode" : "tile", + "width" : 2, + "height" : 2 + }, + "cap-insets" : { + "bottom" : 16, + "top" : 16, + "right" : 16, + "left" : 16 + } + }, + "idiom" : "iphone", + "filename" : "KBKey@2x.png", + "scale" : "2x" + }, + { + "resizing" : { + "mode" : "9-part", + "center" : { + "mode" : "tile", + "width" : 3, + "height" : 3 + }, + "cap-insets" : { + "bottom" : 24, + "top" : 24, + "right" : 24, + "left" : 24 + } + }, + "idiom" : "iphone", + "filename" : "KBKey@3x.png", + "scale" : "3x" + }, + { + "resizing" : { + "mode" : "9-part", + "center" : { + "mode" : "tile", + "width" : 1, + "height" : 1 + }, + "cap-insets" : { + "bottom" : 16, + "top" : 16, + "right" : 16, + "left" : 16 + } + }, + "idiom" : "ipad", + "filename" : "KBKey~ipad.png", + "scale" : "1x" + }, + { + "resizing" : { + "mode" : "9-part", + "center" : { + "mode" : "tile", + "width" : 2, + "height" : 2 + }, + "cap-insets" : { + "bottom" : 32, + "top" : 32, + "right" : 32, + "left" : 32 + } + }, + "idiom" : "ipad", + "filename" : "KBKey@2x~ipad.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Mini vMac/Assets.xcassets/KBKey.imageset/KBKey.png b/Mini vMac/Assets.xcassets/KBKey.imageset/KBKey.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec11c445bcc511121198465d5118cb3ed9c5b6f GIT binary patch literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn0wgD<^cR9CPZ!6K3dXe;wlW@KU~svZz1d04 zbH0$~M62$F(GQFlh?-cK6x!d3y|;D0)t+9Z&2KJ0?K{p`ETZ46ny-6aY5R%YcMchD zbl-DYN_O&v=dlaReSTb+Kl#Gr*oE~zUo_ueNyvc2Xqx{^mHDrm25 zv$OsqTy<+r=yXvI3uOl<8P6r#y_MKr3j8_F^;g?YPq}2)44~r}JYD@<);T3K0RW31 BPOty~ literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBKey.imageset/KBKey@2x.png b/Mini vMac/Assets.xcassets/KBKey.imageset/KBKey@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4f007464c7eae8a0804842f83cff54b7693e7aef GIT binary patch literal 354 zcmV-o0iFJdP)-yWyO>2=d~Tnb*WhC3wO-rM`(dUu(^UC_KSizIE#7Bo{f#o-pTPILw@ zienR<1+C;#T&Yw-P1WIn==#jTjyYxn#vNv3jt;g~rw5|dVQQk1%eN^}#-#>o3OE|b zm5Haa$)RE@qgra9#`_N&FRyB;jEcyiYzp-60^rE;u8&gw%>V!Z07*qoM6N<$f)Ep( AlK=n! literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBKey.imageset/KBKey@2x~ipad.png b/Mini vMac/Assets.xcassets/KBKey.imageset/KBKey@2x~ipad.png new file mode 100644 index 0000000000000000000000000000000000000000..9d4356288fbf55664d991b0a69a9a7419d8ae5ce GIT binary patch literal 574 zcmV-E0>S->P)nL9kmotiQ6|_?51@;qd`GKkd&g0B)kavx zmp|_6+g0bQ_%Y9|XTR=Sb$t6L>_Se-WfIMk6jt|GnqRp8r1df@LE3bZqJ=nm}-s3C~rr@;HKXj;ldf zhr$py)FA9@2+n<_2H`$OaK#Ub5bqIO#Zq))S_sY)Y7m|ihHzC)BQ!!IG(saZLL)Rn zBQ!!I{#Qgrs3BAYf-C+{2S<5Kqq{xQjc~ zATI4ha9h`_L2TZD;Fb=TO!H5pQZ}Pp$D<*LvCo@sr}8oQcC8j{X^@J zLmy(-gB0@pt7b<}?SFLs{R8{0_*AxQIY<>zww#aF>v#9=pFX*NcC>#L@<^cvHPsFS z8RVO`3=FiRj#|+R1H)K^1X9Q%SHqL$kVOhBun>bV(2G{okxr-k1`Auk5y^xaMF0Q* M07*qoM6N<$f>s~}nE(I) literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBKey.imageset/KBKey@3x.png b/Mini vMac/Assets.xcassets/KBKey.imageset/KBKey@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e95e4df38a3fa2c58600de0df6d713a700080dbc GIT binary patch literal 497 zcmV_6Ob^Vo*Mx+QdMqSV6^Dt#{O&OJm)wmtict(C%_XWaRYg#pojFbwXO6>z zDC6%kRgIhRcOe7OAacbiI`+pEr;x)SWbhCjInY-icjN#fcxY(^A)?|`udOrH;uIo; z7(p9p6cG8l&K$RmZnx{qaeo&g6p%&-Mn45YG`6iWbLRNND{mjw-Oyf38$ZQEwI{jy6ow%NsHy4bAZqzS4xRoKP0ZD;J6FVB?c$=G`T_5Iy*0U8}% z!a%IRVcbOu(j-L1W8A=A%*C&gffv{%q6--UCZ8oM0aM5jQ7}}(2?RZoFP5B$j?O?% z5UdO!j=O*t2!hh893Xxm?stM-^pVtPfTSK1c#zLF`h~gN6X3+ebEH5)S*04<46!KNy-8zn;#w%#Jewhv&s== z$#4#cowxH+9N{tQO5hco*|mGtRryQ()GZEG$|q)W>Gb`L(ViyXDEI^tcOKruFR%q? oa0NFBxq=JW!$0@|x4{&53NP}0Kh!K7Qvd(}07*qoM6N<$f+P5p5C8xG literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBKeyDark.imageset/Contents.json b/Mini vMac/Assets.xcassets/KBKeyDark.imageset/Contents.json new file mode 100644 index 0000000..dff8b2e --- /dev/null +++ b/Mini vMac/Assets.xcassets/KBKeyDark.imageset/Contents.json @@ -0,0 +1,103 @@ +{ + "images" : [ + { + "resizing" : { + "mode" : "9-part", + "center" : { + "mode" : "tile", + "width" : 1, + "height" : 1 + }, + "cap-insets" : { + "bottom" : 8, + "top" : 8, + "right" : 8, + "left" : 8 + } + }, + "idiom" : "iphone", + "filename" : "KBKeyDark.png", + "scale" : "1x" + }, + { + "resizing" : { + "mode" : "9-part", + "center" : { + "mode" : "tile", + "width" : 2, + "height" : 2 + }, + "cap-insets" : { + "bottom" : 16, + "top" : 16, + "right" : 16, + "left" : 16 + } + }, + "idiom" : "iphone", + "filename" : "KBKeyDark@2x.png", + "scale" : "2x" + }, + { + "resizing" : { + "mode" : "9-part", + "center" : { + "mode" : "tile", + "width" : 3, + "height" : 3 + }, + "cap-insets" : { + "bottom" : 24, + "top" : 24, + "right" : 24, + "left" : 24 + } + }, + "idiom" : "iphone", + "filename" : "KBKeyDark@3x.png", + "scale" : "3x" + }, + { + "resizing" : { + "mode" : "9-part", + "center" : { + "mode" : "tile", + "width" : 1, + "height" : 1 + }, + "cap-insets" : { + "bottom" : 16, + "top" : 16, + "right" : 16, + "left" : 16 + } + }, + "idiom" : "ipad", + "filename" : "KBKeyDark~ipad.png", + "scale" : "1x" + }, + { + "resizing" : { + "mode" : "9-part", + "center" : { + "mode" : "tile", + "width" : 2, + "height" : 2 + }, + "cap-insets" : { + "bottom" : 32, + "top" : 32, + "right" : 32, + "left" : 32 + } + }, + "idiom" : "ipad", + "filename" : "KBKeyDark@2x~ipad.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Mini vMac/Assets.xcassets/KBKeyDark.imageset/KBKeyDark.png b/Mini vMac/Assets.xcassets/KBKeyDark.imageset/KBKeyDark.png new file mode 100644 index 0000000000000000000000000000000000000000..fa2c119e5edc6e222d77b3d8c94670f4007fb1e2 GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn0wgD<^cMoDxt=bLAr*{kFKlHz6d=<2@OE13 zn#OOM6NOf8mUx%bV0~vhvx!B>$^!LwCx6~vZ@7$A*eEKPkGZ}SR-9Jre zcNdg;KJ~#}i{q0odd=ktx0xAxl{2nl5#PO?J|7nGrG?M?vi;SP{f|BOuJn7QJI~;u z;Bpq_K2B!)Q>VkK{@(pBzvoNY({T(R)sk!^ITxd^q$^-=>WcO zmQ+l_3EV;#3$r-8xNDq+(am<)6qA-zkSsm9ScOq`k-Qkis-E1$#0114N$i5N5Q>FVfga5RDaMJOsuxHXspNM|qIe zQ4oNnxX3cRkuEX3QDlK)P>5J$YaLC!DU7v_7TG`q#biPSm`m;T{ln{X=dvFoPk&w* zk$tnLgKPEurFJkw1!MvwVni^4IYo^iJ|}33L4a~lgAR;fdKhQ|Bj`X4WgyVAp!v@w X#&dFBve?mY00000NkvXXu0mjf>@}Z& literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBKeyDark.imageset/KBKeyDark@2x~ipad.png b/Mini vMac/Assets.xcassets/KBKeyDark.imageset/KBKeyDark@2x~ipad.png new file mode 100644 index 0000000000000000000000000000000000000000..789f5b145192b746bc77fa707eeaa383a04f2b31 GIT binary patch literal 579 zcmV-J0=)f+P)YQ4SWN4 z0r0|;>Yk=(51l53K2aZyQ;Dp|E7628Kwos-xM;LLO-Xf#pVnZs(+7kV&@cB{8ze zq(xEQ3j@ChNimFp(D#PX{)><(qdnxH)W=jW>FHyE2kg_7?l3?&PgC$u=~6uwQ##?_ zlbdW1kFLPK$FCV7Ucx_dkPSlYgMackBg7c|Q!F+J*@Azn%Lbu3K?sl4{#OJ?a0Ewi z1V?ZLM{op3a0EvvE*pa4!aw;>_!%g&`awJi{~ispK|CIYe|J8xLEQZW|1OGb5LZ_Y~%2(1Z)^}EH%BwmR1yHP+BBGrn5uxkGW$*klN zvHyaw3I((xa!tsf*s^SM`~C};^gEv)kBF199p^<-T-kau@o?z&#|!el3!B?pmZ69Y znh>j62*{yWvE?A37G+e8W(b&#c}O9HJPI-Hw17M^Sc18j2?5QhMj7#Vyl<3XkweD! Rl{^3d002ovPDHLkV1gy~4h{eS literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBKeyDark.imageset/KBKeyDark@3x.png b/Mini vMac/Assets.xcassets/KBKeyDark.imageset/KBKeyDark@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..805487ba19c48528e5d43522042a6fa75a566c02 GIT binary patch literal 531 zcmV+u0_^>XP)wqd9(X?JyVeHf4tcZaYcb5!^WF_}RG68Q9;!rz?%)mg zcYOVg0}vG`^ctITX3Cs5_vkr&pb}NST8TcIm-K)xnLU&@cA*!|IvS?Dc}JCIX5LZ$ zXcmF6W?TA6uiR-iH3r7~4oURk>hok7?Mkm18yj?D{c{@1PGW|g&qCd=80lqV|Lnbq{0~T1(*hcTI#5)t)h$vW{NVw_t z{}H%pB+v;SEU4^ATcL7x1PT^v>VX60roFeVUM@Ec1qVH-BZ3rEY~#=l{h`^XYiLJo zBNS4IpaHW#g`j4x?aL1Bm^ydh_VE|qTjPIsDNEY--W+|l|K|9q!Rfy2%(YOTBeT(< z2pp)E<{*M5^uR)*$wUGcdeF2^L}6h87Go70B$0ypYAGaOVHsSQ*Gw05{IP>AmCoJ&enRjO%pE+^5|BMD!11?CPJ$mNz=`W|gp8SRnp89(F z%jpkivaq<|)NzDn!tl)Jvq5M^oK6B6Kngs5`#&>^5eIf*Dk9{e6Iv)n9C-*7CK2w4 zLowp`2hxq$T#sVJ$*-gvv924{h;O9BHGPA@h_(HrMvNLkkJNeMD;Y-gQk>aWw4)fY z?;hz!EG|beV&h)Yjpz(OF=A>9sRrzSqAZ4Dgp~U38>AUg-^#$qfC8B8BZ)WU#Fw7g z46+OufRoR&b=t;L2cI7Mh$D)>9{+s!)z%C1H+khVs4`#$Obkj4whTTDK@1@bp_mXT l8qDCw;K*Rez{fBg0RX=y@BON1Lx%tW002ovPDHLkV1hjbmURFC literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBNumPad.imageset/Contents.json b/Mini vMac/Assets.xcassets/KBNumPad.imageset/Contents.json new file mode 100644 index 0000000..60d8cbd --- /dev/null +++ b/Mini vMac/Assets.xcassets/KBNumPad.imageset/Contents.json @@ -0,0 +1,32 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "1x" + }, + { + "idiom" : "iphone", + "filename" : "KBNumPad@2x.png", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "filename" : "KBNumPad@3x.png", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "filename" : "KBNumPad~ipad.png", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "filename" : "KBNumPad@2x~ipad.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Mini vMac/Assets.xcassets/KBNumPad.imageset/KBNumPad@2x.png b/Mini vMac/Assets.xcassets/KBNumPad.imageset/KBNumPad@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..01bd96214d1c7b6230f85aed4a4f9b6b6188d68d GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX0wgC|rfCAHKu;IPkP61+1tLETZIXZZOR>EA z(2!Bb_h`~WS(XDnj~HXlFSy9_k+<&JflRp)Ml-2|#f)y*XJ5GTI^`5(%B^Bmc-mU0 laF~12R=czhNeg%w7_RW73#kgXYy}$5;OXk;vd$@?2>^_`E7Je~ literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBNumPad.imageset/KBNumPad@2x~ipad.png b/Mini vMac/Assets.xcassets/KBNumPad.imageset/KBNumPad@2x~ipad.png new file mode 100644 index 0000000000000000000000000000000000000000..332186cd398d62c71033771e54b1829746148fcb GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^`ao>N!2%@H(!7#@RHdhjV@L(#+Z*OgOo|LH7vl?+ z)_!TYb)=lIW-)L0iCDB5lX4{&%$1Zez?3Js zLY_<^Ay-NQhiVYvBZWs)IS-Im=^{zDv9>Q^UM+8DGAkzk0J#(pVhi3o)002ovPDHLkV1h)5SepO< literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBNumPad.imageset/KBNumPad~ipad.png b/Mini vMac/Assets.xcassets/KBNumPad.imageset/KBNumPad~ipad.png new file mode 100644 index 0000000000000000000000000000000000000000..0722c85faa86912e1fa77007f6871434d0d3d457 GIT binary patch literal 136 zcmeAS@N?(olHy`uVBq!ia0vp^q9Dw{0wkH`a%F*(zo(01NCo5DbK6Ck6$M%zuGgz< z{q*;^QTnVIs~fgo6nrV4V0+-S%2GA`v}jZIc&02T2bWpfoL~Q6U;F>o9@DRDv$tMr knZI||=El{IUbQje<`IuNw;j-43pAR+)78&qol`;+00v?)-T(jq literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBOption.imageset/Contents.json b/Mini vMac/Assets.xcassets/KBOption.imageset/Contents.json new file mode 100644 index 0000000..5b949a1 --- /dev/null +++ b/Mini vMac/Assets.xcassets/KBOption.imageset/Contents.json @@ -0,0 +1,32 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "1x" + }, + { + "idiom" : "iphone", + "filename" : "KBKeyOption@2x.png", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "filename" : "KBKeyOption@3x.png", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "filename" : "KBOption~ipad.png", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "filename" : "KBOption@2x~ipad.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Mini vMac/Assets.xcassets/KBOption.imageset/KBKeyOption@2x.png b/Mini vMac/Assets.xcassets/KBOption.imageset/KBKeyOption@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5bb885fc1414a647e54d68abad21edfe1540103c GIT binary patch literal 172 zcmeAS@N?(olHy`uVBq!ia0vp^8bGYV!2%=$O*hO1QpKJwjv*C{$qVcvj%{FMe{*0_ z6uU+r|GK1vA4fjOtD77$xWK5<#sBX>{sXllo;#-tn3&@ZJatp}&k|Q5P`$&*Fs6O? zL@x!dlf6@#1I_z(6<+CGoF`NIc!jk4U5i)8(%QIqBAk{<7ao!q**S4TZj5IQ%8(c>P&&$@ftg;H0_Y<#Mr9@@klzLIrg?@qPSEB*Rg=GCv6 zJ--Bqi*;sP?q7MaXWO(hqO&}&R_?HRzWcSR{+$pb_48@7bGJp#J!^J5XEQSKUefio s{ZSo}d8uN3_mlZP$Q3typK+LX+T6-O6^X~+fUaZkboFyt=akR{0E!M&MgRZ+ literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBOption.imageset/KBOption@2x~ipad.png b/Mini vMac/Assets.xcassets/KBOption.imageset/KBOption@2x~ipad.png new file mode 100644 index 0000000000000000000000000000000000000000..3bc0e82c71add8b15d981a5077f4b941310389d8 GIT binary patch literal 365 zcmV-z0h0cSP)Dr(cXxMpO;?bLyF)?JHE36Wxuz{`Wo~s^=3AWmm|K|}-d>#Zd&~Fx*H0b{ z!!QiPFbrX=AkXe%n1y}Y9eC*>e^|#i2NsNdhaGsw=5&DUrgp#x2fP!on%e)aM=sZ~ zg#Xz?t=+xCX)9=zgfvtfXF?KQqvDAKeC9kVq9kE}Ehv~vnIx1m7X_Ockc4PdoaeJ7 zJVV7B37F(0Dl#PD11nLmlmF(wJ`P;o~BzH%8A;gT@KZWPR>KoTlhh=TR> zNJ2a+EPRp#D=MrK@DU3t5+$LB4JcSdwImcW2L*c>mV`)DT;;1I+((7K1dMY81+&PL zgcgGTyNh_mYC8Dl&}?|gS*CRZ;DvSo#A*l3V4mI0LMSr~!!W)B9|?gPuf!AC00000 LNkvXXu0mjfqXdyH literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBOption.imageset/KBOption~ipad.png b/Mini vMac/Assets.xcassets/KBOption.imageset/KBOption~ipad.png new file mode 100644 index 0000000000000000000000000000000000000000..2b6604617bcc0b52832655e669afdd228dd9e3a7 GIT binary patch literal 263 zcmV+i0r>ujP)kdg0002aNklD^m?YLyz8g>zPdr9a1&Bg!Ox@a_>5G0WsG4%Lo6fQlZ~lJ1{pSKi~{Tm zVjJOz2$ZFuLzu@OW#xPzjG&HQgip#*=Ep38j#QL|v5&Az2+GoVK$yV`WwrbvG^37b zglFP000&U1ONa4pSJ%!0001*Nkl#97i^fj$`=Q5?NOq*p7hMiTwj=OV N002ovPDHLkV1lQYP)UbY!8ZYO=t_i+hLGR;h0eq(lzEs ztTG`n0+C-~u7tn{gaczNe*z*S5c%c*8Dr5(D2zbl7nnDpFajYm#+)$lIp#?Sj4>mK z&oF^d7=ch2Zz4a%{wAh`yaOW;A~V*B7G^9E3L_9IGs=YHM~q;P;QRq>+>s5OxFaV} zapwqpaZN=_Vc;DVq#y+;$lD;|MMJiOkgbNS2O(<>nefn?cwmCq4wp215L*7A;gX7$ YB62H!o3H2bZ~y=R07*qoM6N<$f+0+5v;Y7A literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBShiftDown.imageset/KBShiftDown~iphone@2x.png b/Mini vMac/Assets.xcassets/KBShiftDown.imageset/KBShiftDown~iphone@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1807106afacf2b91be96d10327493333967f7461 GIT binary patch literal 257 zcmV+c0sj7pP)unlay9y3@%&{j-II5DNp z?Zi|AZkZ14#Z*IXg*NTSln(A7rgU&ea;ojjoIA!*qGE0pU^=FhXV8LEJWV@+Nq)3Y zU$khOCz!B4*2feu7jf@Bu_wgc_rxv`H^>!F9id0g8K6x*_Q-rE2G~-A00000NkvXX Hu0mjfTgPU~ literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBShiftDown.imageset/KBShiftDown~iphone@3x.png b/Mini vMac/Assets.xcassets/KBShiftDown.imageset/KBShiftDown~iphone@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a23e507b71de1b64f4276fd70b5dbfcc414d9004 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^mOyO8!2%?GuI)a{z`)4p>Eakt!T9#xFE3D$?4 z;^h_pv=*r(?P0c3P**;DH}=#4F}_df7U6y+>0CcNBDfEiB;_>l9#K@4-_b4bsACfQ z9qtRQd>iCvaLYbnsQocx662Qz&N6HdoS3KZ%Rb^zl#k-N&_2z=?L_-D<}V9V{RBQO zoXSv}(R#ThVZqb=O;>^Jsm3ie8M+SgQIBPS%AYeS%1bS%$!PWliZ*;&$h!`xv`1m<$e>QtJ(Q?-hMO7gyC)78&q Iol`;+0M4>}#sB~S literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBShiftUp.imageset/Contents.json b/Mini vMac/Assets.xcassets/KBShiftUp.imageset/Contents.json new file mode 100644 index 0000000..ba0f658 --- /dev/null +++ b/Mini vMac/Assets.xcassets/KBShiftUp.imageset/Contents.json @@ -0,0 +1,32 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "1x" + }, + { + "idiom" : "iphone", + "filename" : "KBShiftUp~iphone@2x.png", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "filename" : "KBShiftUp~iphone@3x.png", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "filename" : "KBShiftUp~ipad.png", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "filename" : "KBShiftUp~ipad@2x.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Mini vMac/Assets.xcassets/KBShiftUp.imageset/KBShiftUp~ipad.png b/Mini vMac/Assets.xcassets/KBShiftUp.imageset/KBShiftUp~ipad.png new file mode 100644 index 0000000000000000000000000000000000000000..54d346793637cd3ddb63ffa8a9909dd48d96b9ec GIT binary patch literal 305 zcmV-10nYx3P)x|!FT zCnW5VhAwO|q89Y_5s7-xfT^Y)kf3`Swph)GTGZEjM0Ur)R8x0|=%!_QXjYG6v>E&Z2ZUHnW;Wihlu;4F8QJ^f-)xPCt1cN z1qy7Z;w~sqNJSdw6u!)6EO#nshpTwfI2N)C7$RAZ<@Nz$N#jtL4^V({t<_aBcHnRk z{6>VYr2YPJal#r3MQLz<;n2KWNuO3r(-PhgQ7W=+loC+!KX>c_6sG nAaH@;TG;2MCicWO%{UbS;xkEJT!r<>00000NkvXXu0mjfXv2~L literal 0 HcmV?d00001 diff --git a/Mini vMac/Assets.xcassets/KBShiftUp.imageset/KBShiftUp~iphone@3x.png b/Mini vMac/Assets.xcassets/KBShiftUp.imageset/KBShiftUp~iphone@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..b7527357520d8a3d8eea13cc75c7d6200badaef0 GIT binary patch literal 580 zcmV-K0=xZ*P)8Q4C+qP}nwr$(ixMQQXZQHip{@X>{_de9W-3{-%XEI8%I252dW2nMU z6<{uJKKx83@bjW`-X|6QNzqwK`X5$whH?*dYm(p5X-cgk1{-v5f}hbT%B&+C5Q)ut zWE?)3ti&cF@=;`V=+QCwV3O8vCo)J>_UM^W{zZ2Yg`i@xU(b%fLld-iA29$?_^>91 zQ<_rn>v1DA( ziGesYOe-#s6f-Hgq*MLfMK6*9b7{GzlYOx?L`ScZ8ao-duG77-Fj&W~lOB6nxMd$6 zs3SMYj59g7t1Ue-&|l+s$%b=zP?+xgKwlIk7w#0`p|*A9J9=yE0r_#S2#>U}3*XR7 zBabMIH%jv4)6Um)MueUyN(sDEmS+lgB$)1sKBElYs)WLHK%gs%QUUK(=cU5!`J}C) zt#YNIgq0kY_Um(;>PjnsKY23VnJ1N_yD*PmKT;S_HSevBDyI|e&`L3RT){{`7P z0NuIq6SLQ8#gDkiy7E8!Kl?xHQ}%y0jJMfvn2G91Rv*6dVW#k>`Nz0&PXDziL?pg1 z5=A(s@BXeV=Z&ME`M8paXAYwGNr6kXcw`xUKxI6Tjzt{fzR{~Z%6wAsLvH~omYdI> SC5=J=0000 + +extern const NSUInteger KBKeyEventStickyKey; + +@interface KBKey : UIButton + +@property (nonatomic, copy, nullable) NSString *label; +@property (nonatomic, assign) int16_t scancode; +@property (nonatomic, assign) BOOL dark; + +@end + +@interface KBStickyKey : KBKey +@property (nonatomic, assign) BOOL down; +@end + +@interface KBHideKey : KBKey + +@end + +@interface KBShiftCapsKey : KBStickyKey + +@property (nonatomic, assign) BOOL capsLocked; + +@end diff --git a/Mini vMac/KBKey.m b/Mini vMac/KBKey.m new file mode 100644 index 0000000..0f16366 --- /dev/null +++ b/Mini vMac/KBKey.m @@ -0,0 +1,196 @@ +// +// KBKey.m +// BasiliskII +// +// Created by Jesús A. Álvarez on 16/03/2014. +// Copyright (c) 2014 namedfork. All rights reserved. +// + +#import "KBKey.h" + +const NSUInteger KBKeyControlStateCaps = 1 << 16; +const NSUInteger KBKeyEventStickyKey = 1 << 24; + +@implementation KBKey + +- (id)initWithFrame:(CGRect)frame { + if ((self = [super initWithFrame:frame])) { + self.dark = NO; + self.titleLabel.adjustsFontSizeToFitWidth = YES; + self.titleLabel.lineBreakMode = NSLineBreakByTruncatingTail; + self.titleLabel.minimumScaleFactor = 0.5; + self.titleEdgeInsets = UIEdgeInsetsMake(0, 2, 0, 2); + [self setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; + } + return self; +} + +- (void)awakeFromNib { + self.dark = NO; +} + +- (void)setDark:(BOOL)dark { + _dark = dark; + [self setBackgroundImage:[UIImage imageNamed:@"KBKey"] forState:dark ? UIControlStateHighlighted : UIControlStateNormal]; + [self setBackgroundImage:[UIImage imageNamed:@"KBKeyDark"] forState:dark ? UIControlStateNormal : UIControlStateHighlighted]; +} + +- (void)setLabel:(NSString *)label { + self.titleLabel.numberOfLines = [label containsString:@"\n"] ? 2 : 1; + [self setTitle:label forState:UIControlStateNormal]; +} + +- (NSString *)label { + return [self titleForState:UIControlStateNormal]; +} + +- (void)setTitle:(NSString *)title forState:(UIControlState)state { + if (title.length > 1 && [title hasPrefix:@"@"] && ![title containsString:@"\n"]) { + [super setTitle:nil forState:state]; + NSArray *components = [[title substringFromIndex:1] componentsSeparatedByString:@"/"]; + if (state == UIControlStateNormal) { + if (components.count == 3) { + [super setImage:[UIImage imageNamed:[components[0] stringByAppendingString:components[1]]] forState:UIControlStateNormal]; + [super setImage:[UIImage imageNamed:[components[0] stringByAppendingString:components[2]]] forState:UIControlStateHighlighted]; + } else if (components.count == 1) { + UIImage *image = [UIImage imageNamed:components.firstObject]; + [super setImage:image forState:UIControlStateNormal]; + [super setImage:image forState:UIControlStateHighlighted]; + } else { + NSLog(@"Can't set title for %@: %@", self, title); + } + } else { + [super setImage:[UIImage imageNamed:components.firstObject] forState:state]; + } + self.imageEdgeInsets = UIEdgeInsetsMake(-2, 0, 0, 0); + } else if ((id)title != [NSNull null]) { + [super setTitle:title forState:state]; + [super setImage:nil forState:state]; + } +} + +- (instancetype)_sameKey { + __block KBKey *otherKey = nil; + [self.superview.subviews enumerateObjectsUsingBlock:^(KBKey *obj, NSUInteger idx, BOOL *stop) { + if (obj != self && [obj isKindOfClass:[self class]] && obj.scancode == self.scancode) { + otherKey = obj; + *stop = YES; + } + }]; + return otherKey; +} + +@end + +@implementation KBHideKey + +@end + +@implementation KBStickyKey { + @protected + BOOL _down; +} + +- (id)initWithFrame:(CGRect)frame { + if ((self = [super initWithFrame:frame])) { + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)]; + [self addGestureRecognizer:tap]; + } + return self; +} + +- (UIControlState)state { + return [super state] | (_down ? UIControlStateHighlighted : 0); +} + +- (void)setDown:(BOOL)down { + if (_down != down) { + _down = down; + [self sendActionsForControlEvents:KBKeyEventStickyKey]; + [self setNeedsLayout]; + + KBStickyKey *otherKey = [self _sameKey]; + if (otherKey != nil) { + otherKey->_down = down; + [otherKey setNeedsLayout]; + } + } +} + +- (void)tap:(UIGestureRecognizer *)gestureRecognizer { + if (gestureRecognizer.state == UIGestureRecognizerStateRecognized) { + self.down ^= YES; + } +} + +@end + +@implementation KBShiftCapsKey { + BOOL wasCapsLocked; +} + +- (id)initWithFrame:(CGRect)frame { + if ((self = [super initWithFrame:frame])) { + self.dark = YES; + [self setImage:[UIImage imageNamed:@"KBShiftUp"] forState:UIControlStateNormal]; + [self setImage:[UIImage imageNamed:@"KBShiftDown"] forState:UIControlStateHighlighted]; + [self setImage:[UIImage imageNamed:@"KBCapsLock"] forState:KBKeyControlStateCaps]; + [self setImage:[UIImage imageNamed:@"KBCapsLock"] forState:KBKeyControlStateCaps | UIControlStateHighlighted]; + [self setBackgroundImage:[UIImage imageNamed:@"KBKey"] forState:KBKeyControlStateCaps]; + [self setBackgroundImage:[UIImage imageNamed:@"KBKey"] forState:KBKeyControlStateCaps | UIControlStateHighlighted]; + + UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTap:)]; + doubleTap.numberOfTapsRequired = 2; + [self addGestureRecognizer:doubleTap]; + } + return self; +} + +- (void)setTitle:(NSString *)title forState:(UIControlState)state { + // nothing here +} + +- (void)setDark:(BOOL)dark { + [super setDark:YES]; +} + +- (UIControlState)state { + return [super state] | (_capsLocked ? KBKeyControlStateCaps : 0); +} + +- (void)tap:(UIGestureRecognizer *)gestureRecognizer { + if (gestureRecognizer.state == UIGestureRecognizerStateRecognized) { + wasCapsLocked = self.capsLocked; + typeof(self) sameKey = [self _sameKey]; + if (sameKey != nil) { + sameKey->wasCapsLocked = wasCapsLocked; + sameKey.capsLocked = NO; + } + self.capsLocked = NO; + if (wasCapsLocked) { + _down = YES; + self.down = NO; + } else { + self.down = !self.down; + } + [[self _sameKey] setNeedsLayout]; + } +} + +- (void)doubleTap:(UIGestureRecognizer *)gestureRecognizer { + if (gestureRecognizer.state == UIGestureRecognizerStateRecognized) { + self.down = NO; + self.capsLocked = !wasCapsLocked; + typeof(self) sameKey = [self _sameKey]; + if (sameKey != nil) { + sameKey->wasCapsLocked = wasCapsLocked; + sameKey.capsLocked = self.capsLocked; + } + [self sendActionsForControlEvents:KBKeyEventStickyKey]; + [self setNeedsLayout]; + wasCapsLocked = YES; + [sameKey setNeedsLayout]; + } +} + +@end \ No newline at end of file diff --git a/Mini vMac/KBKeyboardLayout.h b/Mini vMac/KBKeyboardLayout.h new file mode 100644 index 0000000..6ad40c3 --- /dev/null +++ b/Mini vMac/KBKeyboardLayout.h @@ -0,0 +1,26 @@ +// +// KBKeyboardLayout.h +// BasiliskII +// +// Created by Jesús A. Álvarez on 09/04/2016. +// Copyright © 2016 namedfork. All rights reserved. +// + +#import + +#define VKC_SHIFT_CAPS -127 +#define VKC_HIDE -128 + +@interface KBKeyboardLayout : NSObject + +@property (nonatomic, readonly) NSString *identifier; +@property (nonatomic, readonly) NSString *name; +@property (nonatomic, readonly) uint8_t type; +@property (nonatomic, readonly) NSUInteger numberOfKeyPlanes; +@property (nonatomic, readonly) NSArray *availableSizes; + +- (instancetype)initWithContentsOfFile:(NSString*)path; +- (NSString*)labelForScanCode:(NSInteger)scanCode; +- (BOOL)enumerateKeysForSize:(CGSize)size plane:(NSUInteger)plane transform:(CGAffineTransform)transform usingBlock:(void(^)(int8_t scancode, CGRect frame, CGFloat fontScale, BOOL dark, BOOL sticky))block; + +@end diff --git a/Mini vMac/KBKeyboardLayout.m b/Mini vMac/KBKeyboardLayout.m new file mode 100644 index 0000000..34b00f5 --- /dev/null +++ b/Mini vMac/KBKeyboardLayout.m @@ -0,0 +1,433 @@ +// +// KBKeyboardLayout.m +// BasiliskII +// +// Created by Jesús A. Álvarez on 09/04/2016. +// Copyright © 2016 namedfork. All rights reserved. +// + +#import "KBKeyboardLayout.h" + +#if defined(CGFLOAT_IS_DOUBLE) && CGFLOAT_IS_DOUBLE +#define CGFloatValue doubleValue +#else +#define CGFloatValue floatValue +#endif + +@interface KBKeyDescriptor : NSObject +{ + @public + CGRect frame; + CGFloat fontScale; + int8_t scancode; + BOOL dark, sticky; +} +@end + +@interface KBKeyboardLayoutBinaryReader : NSObject +{ + NSData *data; + const void *ptr; +} + +@property (nonatomic, assign) NSUInteger position; + ++ (BOOL)validBinaryHeader:(NSData*)data; +- (instancetype)initWithData:(NSData*)layoutData; +- (NSString*)readString; +- (uint8_t)readUInt8; +- (int8_t)readInt8; +- (uint16_t)readUInt16; +- (int16_t)readInt16; +- (uint32_t)readUInt32; +- (CGFloat)readFloat; +- (CGRect)readFrameOfSize:(NSUInteger)size withLastFrame:(CGRect)lastFrame; +- (NSArray*)readStrings:(NSUInteger)count emptyStringMarker:(id)emptyStringMarker; + +@end + +typedef NSArray KBKeyPlane; +typedef NSMutableArray KBMutableKeyPlane; +typedef NSArray KBKeyMap; +typedef NSMutableArray KBMutableKeyMap; + +@implementation KBKeyboardLayout +{ + NSMutableDictionary *labels; + NSArray *keyPlaneLabels; + NSMutableDictionary *keyMaps; +} + +- (instancetype)initWithContentsOfFile:(NSString *)path { + NSData *layoutData = [NSData dataWithContentsOfFile:path]; + if ([KBKeyboardLayoutBinaryReader validBinaryHeader:layoutData]) { + return [self initWithBinaryRepresentation:layoutData]; + } else if (layoutData.length > 1 && memcmp(layoutData.bytes, "{", 1) == 0) { + // possibly JSON + NSError *err = nil; + NSDictionary *layoutDictionary = [NSJSONSerialization JSONObjectWithData:layoutData options:0 error:&err]; + return [self initWithDictionaryRepresentation:layoutDictionary]; + } + return nil; +} + +- (NSUInteger)numberOfKeyPlanes { + return keyPlaneLabels.count; +} + +- (NSArray *)availableSizes { + return keyMaps.allKeys; +} + +- (NSString *)labelForScanCode:(NSInteger)scanCode { + if (scanCode == VKC_HIDE) { + return @"@KBHide"; + } else if (scanCode < 0 && -scanCode <= keyPlaneLabels.count) { + // switch keyplane + return keyPlaneLabels[-scanCode-1]; + } + return labels[@(scanCode)]; +} + +- (BOOL)enumerateKeysForSize:(CGSize)size plane:(NSUInteger)plane transform:(CGAffineTransform)transform usingBlock:(void (^)(int8_t, CGRect, CGFloat, BOOL, BOOL))block { + NSValue *sizeValue = [NSValue valueWithCGSize:size]; + KBKeyPlane *keyPlane = keyMaps[sizeValue][plane]; + for (KBKeyDescriptor *keyDescriptor in keyPlane) { + block(keyDescriptor->scancode, CGRectApplyAffineTransform(keyDescriptor->frame, transform), keyDescriptor->fontScale, keyDescriptor->dark, keyDescriptor->sticky); + } + return keyPlane != nil; +} + +#pragma mark - Binary Reading + +- (instancetype)initWithBinaryRepresentation:(NSData *)layoutData { + if (![KBKeyboardLayoutBinaryReader validBinaryHeader:layoutData]) { + return nil; + } + if ((self = [super init])) { + KBKeyboardLayoutBinaryReader *reader = [[KBKeyboardLayoutBinaryReader alloc] initWithData:layoutData]; + + // read header + _identifier = [reader readString]; + _name = [reader readString]; + _type = [reader readUInt8]; + + // read 128 labels + labels = [NSMutableDictionary dictionaryWithCapacity:128]; + for (int scancode=0; scancode < 128; scancode++) { + NSString *label = [reader readString]; + if (label != nil) { + labels[@(scancode)] = label; + } + } + + // read keyplane names + NSUInteger numberOfKeyPlanes = [reader readUInt8]; + keyPlaneLabels = [reader readStrings:numberOfKeyPlanes emptyStringMarker:@""]; + + // read map index + NSUInteger numberOfMaps = [reader readUInt8]; + NSMutableDictionary *mapIndex = [NSMutableDictionary dictionaryWithCapacity:numberOfMaps]; + NSMutableArray *mapIDs = [NSMutableArray arrayWithCapacity:numberOfMaps]; + for (NSUInteger i=0; i < numberOfMaps; i++) { + uint32_t mapID = [reader readUInt32]; + uint16_t mapOffset = [reader readUInt16]; + mapIndex[@(mapID)] = @(mapOffset); + [mapIDs addObject:@(mapID)]; + } + + // read maps + keyMaps = [NSMutableDictionary dictionaryWithCapacity:numberOfMaps]; + NSMutableDictionary *mapsByID = [NSMutableDictionary dictionaryWithCapacity:numberOfMaps]; + for (NSNumber *mapID in mapIDs) { + CGSize mapSize = CGSizeMake((mapID.integerValue >> 16), (mapID.integerValue & 0x7FFF)); + reader.position = mapIndex[mapID].integerValue; + // read keyplanes + KBMutableKeyMap *keyMap = [KBMutableKeyMap arrayWithCapacity:numberOfKeyPlanes]; + for (NSUInteger planeNumber = 0; planeNumber < numberOfKeyPlanes; planeNumber++) { + NSUInteger numberOfKeys = [reader readUInt8]; + if (numberOfKeys == 0) { + [keyMap addObject:@[]]; + continue; + } + // read keys + KBMutableKeyPlane *keyPlane = [KBMutableKeyPlane arrayWithCapacity:numberOfKeys]; + CGRect lastFrame = CGRectZero; + for (NSUInteger keyNumber = 0; keyNumber < numberOfKeys; keyNumber++) { + uint8_t flags = [reader readUInt8]; + if ((flags & 0xC0) == 0x00) { + // key + KBKeyDescriptor *key = [KBKeyDescriptor new]; + key->dark = (flags & 0x20) == 0x20; + key->sticky = (flags & 0x10) == 0x10; + key->fontScale = (((flags & 0x0C) >> 2) + 1) * 0.25; + key->scancode = [reader readInt8]; + key->frame = [reader readFrameOfSize:(flags & 0x03) + 1 withLastFrame:lastFrame]; + lastFrame = key->frame; + + [keyPlane addObject:key]; + } else if ((flags & 0xC0) == 0x40) { + // include + NSUInteger includePlaneNumber = flags & 0x0F; + uint32_t includeMapNumber = [reader readUInt32]; + CGAffineTransform includeTransform = CGAffineTransformIdentity; + if ((flags & 0x20) == 0x20) { + CGFloat scaleX = [reader readFloat]; + CGFloat scaleY = [reader readFloat]; + includeTransform = CGAffineTransformScale(includeTransform, scaleX, scaleY); + } + if ((flags & 0x10) == 0x10) { + CGFloat translateX = [reader readInt16]; + CGFloat translateY = [reader readInt16]; + includeTransform = CGAffineTransformTranslate(includeTransform, translateX, translateY); + } + NSUInteger numberOfSkips = [reader readUInt8]; + NSMutableSet *skipKeys = [NSMutableSet setWithCapacity:numberOfSkips]; + for (NSUInteger i=0; i < numberOfSkips; i++) { + [skipKeys addObject:@([reader readInt8])]; + } + + KBKeyMap *includeKeyMap = mapsByID[@(includeMapNumber)]; + if (includeKeyMap.count < includePlaneNumber) { + continue; + } + KBKeyPlane *includeKeyPlane = includeKeyMap[includePlaneNumber]; + [includeKeyPlane enumerateObjectsUsingBlock:^(KBKeyDescriptor * _Nonnull oldKey, NSUInteger idx, BOOL * _Nonnull stop) { + if ([skipKeys containsObject:@(oldKey->scancode)]) { + return; + } + KBKeyDescriptor *key = [KBKeyDescriptor new]; + key->dark = oldKey->dark; + key->sticky = oldKey->sticky; + key->frame = CGRectIntegral(CGRectApplyAffineTransform(oldKey->frame, includeTransform)); + key->fontScale = oldKey->fontScale; + key->scancode = oldKey->scancode; + [keyPlane addObject:key]; + }]; + } + } + [keyMap addObject:keyPlane]; + } + mapsByID[mapID] = keyMap; + if ((mapID.integerValue & 0x7FFF0000) != 0) { + // valid size + keyMaps[[NSValue valueWithCGSize:mapSize]] = keyMap; + } + } + } + return self; +} + +#pragma mark - Dictionary Reading + +- (instancetype)initWithDictionaryRepresentation:(NSDictionary *)layoutDictionary { + if ((self = [super init])) { + _identifier = layoutDictionary[@"id"]; + _name = layoutDictionary[@"name"]; + _type = (uint8_t)[layoutDictionary[@"adbType"] integerValue]; + + // read labels + NSDictionary *layoutLabels = layoutDictionary[@"labels"]; + labels = [NSMutableDictionary dictionaryWithCapacity:100]; + [layoutLabels enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull obj, BOOL * _Nonnull stop) { + if (key.integerValue != 0 || [key isEqualToString:@"0"]) { + labels[@(key.integerValue)] = obj; + } + }]; + + // get keyplane labels + NSArray *keyPlaneIDs = [[labels.allKeys filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nonnull evaluatedObject, NSDictionary * _Nullable bindings) { + return [evaluatedObject integerValue] < 0; + }]] sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"self" ascending:NO]]]; + keyPlaneLabels = [labels objectsForKeys:keyPlaneIDs notFoundMarker:@""]; + [labels removeObjectsForKeys:keyPlaneIDs]; + + // read sizes + NSMutableArray *sizes = [layoutDictionary.allKeys filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nonnull evaluatedObject, NSDictionary * _Nullable bindings) { + CGSize size = CGSizeFromString(evaluatedObject); + return !CGSizeEqualToSize(size, CGSizeZero); + }]].mutableCopy; + NSMutableDictionary *sizeToKey = [NSMutableDictionary dictionaryWithCapacity:sizes.count]; + for(NSUInteger i=0; i < sizes.count; i++) { + CGSize size = CGSizeFromString(sizes[i]); + NSValue *sizeValue = [NSValue valueWithCGSize:size]; + sizeToKey[sizeValue] = sizes[i]; + sizes[i] = sizeValue; + } + + // read layouts + keyMaps = [NSMutableDictionary dictionaryWithCapacity:sizes.count]; + for (NSValue *sizeValue in sizes) { + KBMutableKeyMap *keyMap = [NSMutableArray arrayWithCapacity:self.numberOfKeyPlanes]; + for (NSArray *keyPlane in layoutDictionary[sizeToKey[sizeValue]]) { + [keyMap addObject:[self _loadKeys:keyPlane fromDictionary:layoutDictionary withTransform:CGAffineTransformIdentity skip:nil]]; + } + keyMaps[sizeValue] = keyMap; + } + } + return self; +} + +- (KBKeyPlane*)_loadKeys:(NSArray *)keys fromDictionary:(NSDictionary*)layoutDictionary withTransform:(CGAffineTransform)keyTransform skip:(NSSet*)skipKeys { + NSMutableArray *keyPlane = [NSMutableArray arrayWithCapacity:keys.count]; + CGRect frame = CGRectZero; + for (NSDictionary *keyDict in keys) { + // include + NSArray *includeArgs = keyDict[@"include"]; + if ([includeArgs isKindOfClass:[NSArray class]] && includeArgs.count >= 2) { + CGAffineTransform includeTransform = keyTransform; + NSArray *scaleArgs = keyDict[@"scale"]; + if ([scaleArgs isKindOfClass:[NSArray class]] && scaleArgs.count == 2) { + includeTransform = CGAffineTransformScale(includeTransform, scaleArgs[0].CGFloatValue, scaleArgs[1].CGFloatValue); + } + NSArray *translateArgs = keyDict[@"translate"]; + if ([translateArgs isKindOfClass:[NSArray class]] && translateArgs.count == 2) { + includeTransform = CGAffineTransformTranslate(includeTransform, translateArgs[0].CGFloatValue, translateArgs[1].CGFloatValue); + } + NSArray *skipArgs = keyDict[@"skip"]; + + NSString *layoutKey = includeArgs[0]; + NSNumber *planeNumber = includeArgs[1]; + NSArray *addKeys = [self _loadKeys:layoutDictionary[layoutKey][planeNumber.integerValue] fromDictionary:layoutDictionary withTransform:includeTransform skip:skipArgs ? [NSSet setWithArray:skipArgs] : nil]; + [keyPlane addObjectsFromArray:addKeys]; + } + + // frame + NSArray *kvFrame = keyDict[@"frame"]; + frame.origin.x = [kvFrame[0] CGFloatValue]; + if (kvFrame.count > 1 && kvFrame[1] != [NSNull null]) { + frame.origin.y = [kvFrame[1] CGFloatValue]; + } + if (kvFrame.count > 2 && kvFrame[2] != [NSNull null]) { + frame.size.width = [kvFrame[2] CGFloatValue]; + } + if (kvFrame.count > 3 && kvFrame[3] != [NSNull null]) { + frame.size.height = [kvFrame[3] CGFloatValue]; + } + + // key + id scancode = keyDict[@"key"]; + if ([skipKeys containsObject:scancode]) { + continue; + } + KBKeyDescriptor *kd = [KBKeyDescriptor new]; + if ([scancode isEqual:@"hide"]) { + kd->scancode = VKC_HIDE; + } else if ([scancode isEqual:@"shift/caps"]) { + kd->scancode = VKC_SHIFT_CAPS; + } else { + kd->scancode = (int8_t)[scancode integerValue]; + } + kd->sticky = [keyDict[@"sticky"] boolValue]; + kd->dark = [keyDict[@"dark"] boolValue]; + kd->frame = CGRectIntegral(CGRectApplyAffineTransform(frame, keyTransform)); + kd->fontScale = keyDict[@"fontScale"] ? [keyDict[@"fontScale"] CGFloatValue] : 1.0; + + [keyPlane addObject:kd]; + } + + return keyPlane; +} + +@end + +@implementation KBKeyDescriptor + +@end + +@implementation KBKeyboardLayoutBinaryReader + +- (instancetype)initWithData:(NSData *)layoutData { + if ((self = [super init])) { + data = layoutData; + ptr = data.bytes + 12; + } + return self; +} + ++ (BOOL)validBinaryHeader:(NSData*)data { + return data.length > 12 && memcmp(data.bytes, "NFKeyboard\x01\x00", 12) == 0; +} + +- (void)_checkReadableBytes:(NSUInteger)size { + if (ptr + size > data.bytes + data.length) { + @throw [NSException exceptionWithName:NSRangeException reason:nil userInfo:@{@"pos": @(ptr - data.bytes), @"read": @(size), @"length": @(data.length)}]; + } +} + +- (NSUInteger)position { + return ptr - data.bytes; +} + +- (void)setPosition:(NSUInteger)position { + ptr = data.bytes + position; +} + +- (NSString*)readString { + uint8_t stringLength = [self readUInt8]; + if (stringLength == 0) { + return nil; + } + [self _checkReadableBytes:stringLength]; + NSString *string = [[NSString alloc] initWithBytes:ptr length:stringLength encoding:NSUTF8StringEncoding]; + ptr += stringLength; + return string; +} + +- (uint8_t)readUInt8 { + [self _checkReadableBytes:1]; + return *(uint8_t*)ptr++; +} + +- (int8_t)readInt8 { + [self _checkReadableBytes:1]; + return *(int8_t*)ptr++; +} + +- (uint16_t)readUInt16 { + [self _checkReadableBytes:2]; + uint16_t value = OSReadLittleInt16(ptr, 0); + ptr += 2; + return value; +} + +- (int16_t)readInt16 { + [self _checkReadableBytes:2]; + int16_t value = OSReadLittleInt16(ptr, 0); + ptr += 2; + return value; +} + +- (uint32_t)readUInt32 { + [self _checkReadableBytes:4]; + uint32_t value = OSReadLittleInt32(ptr, 0); + ptr += 4; + return value; +} + +- (CGFloat)readFloat { + uint32_t value = [self readUInt32]; + return *(float*)&value; +} + +- (CGRect)readFrameOfSize:(NSUInteger)size withLastFrame:(CGRect)lastFrame { + CGRect frame = lastFrame; + CGFloat *output[4] = {&frame.origin.x, &frame.origin.y, &frame.size.width, &frame.size.height}; + for (int i = 0; i < size; i++) { + uint16_t value = [self readUInt16]; + if (value != 0x7FFF) { + *output[i] = (CGFloat)value; + } + } + return frame; +} + +- (NSArray*)readStrings:(NSUInteger)count emptyStringMarker:(id)emptyStringMarker { + NSMutableArray *strings = [NSMutableArray arrayWithCapacity:count]; + while (count--) { + [strings addObject:[self readString] ?: emptyStringMarker]; + } + return strings; +} + +@end \ No newline at end of file diff --git a/Mini vMac/KBKeyboardView.h b/Mini vMac/KBKeyboardView.h new file mode 100644 index 0000000..3eb0006 --- /dev/null +++ b/Mini vMac/KBKeyboardView.h @@ -0,0 +1,29 @@ +// +// KBKeyboardView.h +// BasiliskII +// +// Created by Jesús A. Álvarez on 22/03/2014. +// Copyright (c) 2014 namedfork. All rights reserved. +// + +#import +#import "KBKeyboardLayout.h" + +@class KBKey; + +@protocol KBKeyboardViewDelegate +- (void)keyDown:(int)scancode; +- (void)keyUp:(int)scancode; +@optional +- (void)hideKeyboard:(nullable id)sender; +@end + +@interface KBKeyboardView : UIView + +@property (weak, nonatomic, nullable) id delegate; +@property (nonatomic, strong, nullable) KBKeyboardLayout *layout; + +@property (nonatomic, readonly, nonnull) NSArray* keys; +@property (nonatomic, readonly, nonnull) NSArray* stickyKeys; + +@end diff --git a/Mini vMac/KBKeyboardView.m b/Mini vMac/KBKeyboardView.m new file mode 100644 index 0000000..cabbf31 --- /dev/null +++ b/Mini vMac/KBKeyboardView.m @@ -0,0 +1,217 @@ +// +// KBKeyboardView.m +// BasiliskII +// +// Created by Jesús A. Álvarez on 22/03/2014. +// Copyright (c) 2014 namedfork. All rights reserved. +// + +#import "KBKeyboardView.h" +#import "KBKey.h" + +#define KC_COMMAND 55 +#define KC_SHIFT 56 +#define KC_CAPSLOCK 57 +#define KC_OPTION 58 +#define KC_CONTROL 59 + +@implementation KBKeyboardView { + NSMutableArray *keyPlanes; + NSMutableSet *modifiers; + NSMutableIndexSet *keysDown; + CGAffineTransform defaultKeyTransform; + CGFloat fontSize; + CGSize selectedSize; +} + +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + self.backgroundColor = [UIColor colorWithRed:0xEB / 255.0 green:0xF0 / 255.0 blue:0xF7 / 255.0 alpha:0.9]; + modifiers = [NSMutableSet setWithCapacity:4]; + keysDown = [NSMutableIndexSet indexSet]; + } + return self; +} + +- (void)setLayout:(KBKeyboardLayout *)layout { + if ([_layout isEqual:layout]) { + return; + } + _layout = layout; + + // find preferred size (same width or smaller) + CGFloat frameWidth = self.frame.size.width; + CGFloat preferredWidth = frameWidth; + selectedSize = CGSizeZero; + for (NSValue *key in layout.availableSizes) { + CGSize size = key.CGSizeValue; + if (!CGSizeEqualToSize(size, CGSizeZero) && size.width > selectedSize.width && size.width <= preferredWidth) { + selectedSize = size; + } + } + + // try sideways + if (CGSizeEqualToSize(selectedSize, CGSizeZero)) { + preferredWidth = self.frame.size.height; + for (NSValue *key in layout.availableSizes) { + CGSize size = key.CGSizeValue; + if (!CGSizeEqualToSize(size, CGSizeZero) && size.width > selectedSize.width && size.width <= preferredWidth) { + selectedSize = size; + } + } + } + + // still not found + if (CGSizeEqualToSize(selectedSize, CGSizeZero)) { + return; + } + + defaultKeyTransform = CGAffineTransformIdentity; + fontSize = [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone ? 22.0 : 30.0; + if (preferredWidth != selectedSize.width || preferredWidth != frameWidth) { + // adjust width + if (frameWidth / selectedSize.width > 2 || frameWidth / selectedSize.width < 0.5) { + // iphone keyboard on ipad? + defaultKeyTransform = CGAffineTransformMakeScale(frameWidth / selectedSize.width, frameWidth / selectedSize.width); + } else if (frameWidth < selectedSize.width) { + // shrink keyboard + defaultKeyTransform = CGAffineTransformMakeScale(frameWidth / selectedSize.width, 1.33333); + } else { + // iPhone keyboard on bigger phone + CGFloat wScale = self.frame.size.width / selectedSize.width; + defaultKeyTransform = CGAffineTransformMakeScale(wScale, 1.0); + } + } + self.frame = CGRectMake(0, 0, self.frame.size.width, selectedSize.height * defaultKeyTransform.d); + + // init keyplanes array + NSUInteger numberOfKeyPlanes = layout.numberOfKeyPlanes; + keyPlanes = [NSMutableArray arrayWithCapacity:numberOfKeyPlanes]; + for (int i = 0; i < numberOfKeyPlanes; i++) { + [keyPlanes addObject:[NSNull null]]; + } + + [self switchToKeyPlane:0]; +} + +- (NSArray *)_loadKeyPlane:(NSUInteger)plane { + NSMutableArray *keyPlane = [NSMutableArray arrayWithCapacity:64]; + [_layout enumerateKeysForSize:selectedSize plane:plane transform:defaultKeyTransform usingBlock:^(int8_t scancode, CGRect keyFrame, CGFloat fontScale, BOOL dark, BOOL sticky) { + KBKey *key = nil; + if (scancode == VKC_HIDE) { + key = [[KBHideKey alloc] initWithFrame:keyFrame]; + [key addTarget:self action:@selector(hideKeyboard:) forControlEvents:UIControlEventTouchUpInside]; + } else if (scancode == VKC_SHIFT_CAPS) { + key = [[KBShiftCapsKey alloc] initWithFrame:keyFrame]; + key.scancode = KC_SHIFT; + [key addTarget:self action:@selector(capsKey:) forControlEvents:KBKeyEventStickyKey]; + } else if (sticky) { + key = [[KBStickyKey alloc] initWithFrame:keyFrame]; + key.scancode = scancode; + [key addTarget:self action:@selector(stickyKey:) forControlEvents:KBKeyEventStickyKey]; + } else { + key = [[KBKey alloc] initWithFrame:keyFrame]; + key.scancode = scancode; + [key addTarget:self action:@selector(keyDown:) forControlEvents:UIControlEventTouchDown]; + [key addTarget:self action:@selector(keyUp:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchDragExit | UIControlEventTouchCancel]; + } + key.dark = dark; + NSString *label = [_layout labelForScanCode:scancode]; + if ([label containsString:@"\n"]) { + fontScale *= [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone ? 0.6 : 0.65; + } + key.label = label; + key.titleLabel.font = [UIFont systemFontOfSize:fontSize * fontScale weight:UIFontWeightRegular]; + [keyPlane addObject:key]; + }]; + return keyPlane; +} + +- (NSArray *)keys { + return [self.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"self isKindOfClass: %@", [KBKey class]]]; +} + +- (NSArray *)stickyKeys { + return [self.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"self isKindOfClass: %@", [KBStickyKey class]]]; +} + +- (void)switchToKeyPlane:(NSInteger)idx { + if (idx < 0 || idx >= keyPlanes.count) { + return; + } + + // remove previous keys + [self.keys makeObjectsPerformSelector:@selector(removeFromSuperview)]; + + // load keyplane + if (keyPlanes[idx] == [NSNull null]) { + keyPlanes[idx] = [self _loadKeyPlane:idx]; + } + + // add keys + for (KBKey *key in keyPlanes[idx]) { + if ([key isKindOfClass:[KBStickyKey class]]) { + // match modifiers + KBStickyKey *stickyKey = (KBStickyKey *)key; + stickyKey.down = [keysDown containsIndex:key.scancode]; + if ([key isKindOfClass:[KBShiftCapsKey class]]) { + KBShiftCapsKey *shiftCapsKey = (KBShiftCapsKey *)key; + shiftCapsKey.capsLocked = [keysDown containsIndex:KC_CAPSLOCK]; + } + } + [self addSubview:key]; + } +} + +- (void)keyDown:(KBKey *)key { + int16_t scancode = key.scancode; + if (scancode >= 0 && ![keysDown containsIndex:scancode]) { + [keysDown addIndex:scancode]; + [self.delegate keyDown:scancode]; + } else if (scancode < 0) { + [self switchToKeyPlane:(-scancode) - 1]; + } +} + +- (void)keyUp:(KBKey *)key { + int16_t scancode = key.scancode; + if (scancode >= 0 && [keysDown containsIndex:scancode]) { + [keysDown removeIndex:scancode]; + [self.delegate keyUp:scancode]; + // up sticky keys + [self.stickyKeys setValue:@NO forKey:@"down"]; + } +} + +- (void)hideKeyboard:(KBHideKey *)key { + if ([self.delegate respondsToSelector:@selector(hideKeyboard:)]) { + [self.delegate hideKeyboard:key]; + } else { + self.hidden = YES; + } +} + +- (void)stickyKey:(KBStickyKey *)key { + if (key.down && ![keysDown containsIndex:key.scancode]) { + [keysDown addIndex:key.scancode]; + [self.delegate keyDown:key.scancode]; + } else if (!key.down && [keysDown containsIndex:key.scancode]) { + [keysDown removeIndex:key.scancode]; + [self.delegate keyUp:key.scancode]; + } +} + +- (void)capsKey:(KBShiftCapsKey *)key { + if (key.capsLocked && ![keysDown containsIndex:KC_CAPSLOCK]) { + [keysDown addIndex:KC_CAPSLOCK]; + [self.delegate keyDown:KC_CAPSLOCK]; + } else if ([keysDown containsIndex:KC_CAPSLOCK]) { + [keysDown removeIndex:KC_CAPSLOCK]; + [self.delegate keyUp:KC_CAPSLOCK]; + } else { + [self stickyKey:key]; + } +} + +@end diff --git a/Mini vMac/Keyboard Layouts/British.nfkeyboardlayout b/Mini vMac/Keyboard Layouts/British.nfkeyboardlayout new file mode 100644 index 0000000000000000000000000000000000000000..b8ea2042277ce1e94857501b5943096c224449e1 GIT binary patch literal 2323 zcmb7_TTIhe9LImZbK3In-$U_!cZyamEp6!qP=SI1MFjDJpw1gbDxyNMEg~oaOXjjf z=VghzWiDYCrkP84S++fx>D!X+Wyvyq+Cs8RG8f~^-e~vzx85+@!yeB6|3BydIp=)8 z-}CFgP2q;oj zxt4eGUfw11b)T%#{ieGg>H%rjgYuwWB3J9h(xq2OhrV8}(M#na-7TH^I$59>$szp! zZ{rquM&HNvyxkIyCi~(&+{jzxN_{Oi$PId#Jge`Q<$9s4)VyV*NcNqlv?OP{)hvSa)}%r-R6f&bHoUSFA@)p1+KC z741#5D@5!eb`ra2rZMA0G!lQh5?*@cC4g3f7Bo6V#>H=3icLqOCweWD=dV5d@(mcez7TtdWAT3E zKi!Q82Z6l}OoaU)KM-Lbwv>}^jgpibH&siYKVaBv>g(Pf>9DpjnNn82Kp&Z^cq=lCJY_nj=q{$Vx4=jZ|)glu2 z(rG^e{uElmGc!TZ7wTo533p9vU0By%5zRA*FB-U0cjh(h&eHGrc5-0 z>uFs{D}^?EO6lRP6e>;OzMXX6ZcYzh;ZW%#ksfZQ!l|v&F>*t$&YS(z&K%g83p?{* zX94WYhn>Z+vlMm~!OpU0c05IL?Y%72zQ2UpzeTF1 zacPbl^0hXQ5s>X5J79PR$X1ZMLGA%rJ1(t##yMzFkli3VLB>EffNTc23uMa{S=cx( z3o_|z36T9DlOP8`RtJP_=VfG`Alt?nIHyu_rnlhULWLvalG}_jB&w=h{&iI)ab$Y8 zeOfm^PcfOvLw3}j54DFt^RAGZb%)f)$&nRt0p*?7LgkjSC5JtK*il4 zWxh|!WJSfTXXWT(wfY^ljWVjaf^3$0REh`H237-W1v>+*8?2{`J%KSCa|No5X$b3v zHu%98Qh8}Rs_QDAACS}j3YY`5GH>=%p>rT~E`-j5&;<}WA3_&H=u!w>1fk0yRA)nI zKFF6q7Jys}(g`wz&6j{$2r45~`9Zlrc|jF|DucudQ031_O#hd}`{bF5L82Q{vv$_F zil0c;27Ra;oZsOqq@B7$jyed~3#t1cbw5NjO|Us`NY&avMnJZM?0|?JAX`E12Dt}h z?F3uEgP*7xeDws0Lsvwmf#sQVJ3NyLOjd8cv7L zVyFEf)OC_4X$Ytx_drHMpCKVP{^GJ)al9{;NcIn8m!5jObPFch$r7(axd$m1liie? z$Sl!sxX`=KVzU5g$0tZX`754-q#aw#(>LWdV>_bFm6vYbG=5yuyY17udNxhNy`w0I zIx&v#$m29aB}X&+Im-rN%1@5+TcV(1P`Jqvx+R<=gJOuHNYIqY_UCOEGua-@W^4S3 zkjphFS~A%>ytjlolkfAKmWKDCb9P^qfpaPw@DZrdeHsF_AlO;>j2tCfCd6rTQ%Err zqA^!a6_vRXK_r*)lI%ov7)9&o50Yb`6Q7@~GfUj?6eKIs9U`sk2c-EvX(w}7J66uw W?6lAJSHAk^#FOlvbu-yL?fwJ)9V{dO literal 0 HcmV?d00001 diff --git a/Mini vMac/Keyboard Layouts/Spanish (ISO).nfkeyboardlayout b/Mini vMac/Keyboard Layouts/Spanish (ISO).nfkeyboardlayout new file mode 100644 index 0000000000000000000000000000000000000000..a93fd5d65e0796abf8990e039b087ed7e4a9d095 GIT binary patch literal 2372 zcmb7_TWnNC9LE3unceQ$!*mPEO;Dk*z}~leX$!R77FxEHLJRZ)m0P!TEmhksyRD^_ z($+-r0fb_rCMrgva#3QM7%x0vs81%E7!wnT51K~fgBZh`54P)f&H_@S4?di8X6F26 z=KH?+@7acUeR8NHl}L9oiDV|fsV~vnojJ17RoB$$w(=%!;u?kh^$9e>f)zYa)wY7Q)+Eee#HF~ji z>N9=nE$P-b$e>;-EA@5KrLUJ2dXdeedx0I`MsBek)Z5YC$923jZ~FPmzkl)~Z|80H zsZ09Qhj{S}*Ne|5Z2IRX(HW6tx=$X`{oKGiQqlpqPLUCj!t7=e|M@^PCtJZ*Ew7-;hG>~AF+qn zOY@BxPbCxSdn@5(P)?tEo7j(L1s8FM!yMr#7jp@h60heCL?q(HB3>fmr6L{?@%19U zfn+=sGHwxECAgwSr^vX4ja#ubF*MMbfc=+YAD+%H#pdx;dJqRT*N%usfxQY$#KR!p z6!8%Dl+&+_la!k_SIba1V)$zq=-HX*w6`-^g2K)uPgpL{kNlp+0{mLu1$s@~&ijrA z`FooUBbtV{VuvI)yl9?tIi0u;+9aW z>trDEuFQ_Ml7p7gA!;VY{iMX#q@<6P^#xXzpIEJ}63X?uP;34W%H1qI&JQqW2RX(q z^bs|Y$~#2L_9-cCAuD??t9f6r@~;*ua6%~WcR~dt(lLG-b5i7)wa`4SqxB`N6x(=- zjCEqAvv}?#J+z0jqZ1q}y)Ck%P2`)|AzfparL*9HdzK!ArG>Dx2$q(>(qdRz21^ga z(nGNH$lsO%MV6c;Jwc9G838RtjO|(SNZL#`;c&YG6k|8WE03{kS*J!Z~H~*Hj~#fAO}JAgB$`` z9TAS*XOVY?9Gm9hj7rIq-G*li`Nl3vZa2!1sH$?swW>etm>{*_2C4bCNv$kowQ3iu72~W{PjWsn`B8Bo1xN|f3eo~{El3y0H6Y!S>=QE^GRND}$?J_C*O;YphsJ0EP0^J;lT);6`pvss=uy$-?7`%_lOFK|q=g_`I z9_O=Q4p3pi1NX!}2nh=zVG$%OfrQ17unZC&hJ=S8;Sq?{O|gX_9|gG%lm}D{8!rd3 z6oe^Kg+X{h1Sd%OK$SsW1*r1-(Ky~THBYQrH^4$klJ1Zhv- zAXhB}v_a-R$lMPR4U=q58#1+akO`0-AUh#q7sytSdqD05xn+{=14gD=5@Zj^E|4jZ z^&p!-HiK*#=iv59$jOdwC-0q1oup?%<|$}{nAeYaLzq{6n;g3f!5%~%jpG?0EhoJO z<=`$dJh^9#vU1O6M{zi1t-?zFa%0>dG{*g7#yCERea6@Bt1nMg5{*?>VT5O^j87kx zmDJ)`!&uLL^q-6+I}>u6m}T~v<3X<(v9a|=Ov;XiasI#HXkoM#x z=|_J?J4)IU%Xsdl+-__~w6*fgl`F=NYi_q=PFK&RX}EV4MNucl@jZSg%~;71b3aen zFieHXRen{tD~5%iT(PUdGd3(nD2W6uX0|^xo;I^Rn9J7q6Ctm6ShSegx`J1Q)y(&P zPRqjk$O&ht%D{Ox7x3dyqlYvEnulN);4^ZB9A=1f=FTETGel#qoGB{vW}-;W^wQsj z>M)AdH5et=P!~QwIcIrk!&8*(NOy#^?r)Ih8>AhzvGznc=d#n@-(UIMuakGP`@3sq Hcdz>w2T4mS literal 0 HcmV?d00001 diff --git a/Mini vMac/Keyboard Layouts/Spanish.nfkeyboardlayout b/Mini vMac/Keyboard Layouts/Spanish.nfkeyboardlayout new file mode 100644 index 0000000000000000000000000000000000000000..271f406c80be87d45f66250692ac6167dac37eb2 GIT binary patch literal 2364 zcmb7_TWnNS6o%K@d!{{ex;q5~L{OnHz}%;EX$!QS7Fwo4pg`nnq)c8pE3pw&Q=!a4XRVAI@d(`PSb5 z|L@h)##lpQup=2ybu!r#XZ7Buz6d%M$zmN}d6PP(kTo}0LiWBeG` z^W(gmYq_;x-_+>IVSNwoPw@uc!Mk`P@8mXawoJaE2W7Dyk}LGEbm$Qo(97gXy;OSi zD(TeM%2j%~JfQoeTVEqxdWkl9S#QTWCwLpTSSLT#CqLv(yghH~)RjL!evY^BX1PwU z;0AGRTrSsFTP8o(z2e$wZse`9TKCJT({JY|(tYuR-ANhME9cfUY^X~dNn{d%mcBq; za-f%FB7Kne>-eEI+0zs6?If~cu&vLqEvHVM$8`bM^SH)|*h}mt_Rt)o;z%N%y0a2~ z49KZ7ZxQ?OTE!(C&qRjSu#B#$oyen1-j<7Gd*PGBtpMnr%nKBEVh(CSoCwuZvg^d&;R7$4JWc z8){`R6gK=d_V;X$ciP&SEUSf$NglIYq#yZBiv{=xc^Bzr@oV078sP8p^D#pjeia+F zDSC~IY_1|@Ase;OHd2LgQufbC6)j;k_c*IL-?Lg45^DK5p_cwG)QU~gAAUz>M_S2F zi|7C~lj001@eL{IWo3PqmE|W^t7?RDz9Q7hzl3r%OSj{F)NCdDn1w!~CQ^9^NacS* zN}JEh*2}8kOIE%WLiwK-%5z<)Kv>$x&Y~ts?imXe@FrSQ)=JTJm&sTsT0Vp4cG82p zI6E@V(ej%jJJLkn>8;W^dPO>l=H0RM04&Xir3J9`AS^9}rNyxHFf2U;OOM>O`BXSgnQy_YI-E_gKjOe+y1e>1_+z zP@J^mpOJQO32VoWv$H-dRNHx>_WU8#-px`qUXs?7Azf<+83)+`vJ+-^fNTZ13*>H) z8!t&)zi|dy0%Q-!E|5u(4IrC9HiK;0EWKMUNtclH6{zAyG~B(obuuiQ}_}9kYhZixiWU{NzCI1<`v5vuK>u+%HJY zxk+kSF{|Y}SS=l6wPJz`iOGkK11UgCkXDcukgGsCL9PVpnqd1Fqob?Hp0^2|;zzd) zqT8;JvfUt6U_-a9W##W?fA}Jfxe8szG>o;Q>q6kYR9W7E?z({Y-^uMb31%M^7tOmP z_5nzk4+#q(;Xz1P2nmZJ;bBO42ofHFSltv`4DwNst3kOzMX~V`5Q{*VB2@^42Si|; zlowP5Tc$P?MCaiA4vv#{>B6fgm1-T34Zjc)%*w$}kswF`7fb0U91la(x31lIP9)dz0)ti^1+kAC2MZCoLzv4(;G7F+91Zjka>l zWJho~6|KTbzDi@>7cl01qsBZwh<(P_?(474R1=L>*It9Aw{njspsa=N6#la3%c(@Ulc-C;DXb09*_!7hA$ va?bKnhNlSGknS*P-QOb3S4lgX&)VTi&Sj^)x3~J4-zIKncXw!JcW3wy%rZiR literal 0 HcmV?d00001 diff --git a/Mini vMac/Keyboard Layouts/US.nfkeyboardlayout b/Mini vMac/Keyboard Layouts/US.nfkeyboardlayout new file mode 100644 index 0000000000000000000000000000000000000000..ca54a930dad08ed6e5b864b991e857729056d079 GIT binary patch literal 2308 zcmb7_TTGK@9LE3u_buh?$4l{ic8XRlEp6$6gHj3#6cGg#3p!5}sfZJbEr_59ESbB} zDK2pbGnbfcPBWKqvuwLC@or1Dnl@{Iy<8sFUDBaf$RfQ&j_L=wg(LEezMmU;r#TTD z7)cHSn}=o`36Zqh5|S^acz5J_i!`skRjbIr{DZIC)po85|iijj=42WTk2vb zVgoTxq~B8)AL=6+OCI4vI)12)_x48nx`-?owDb@3#QWs*`O7%2;CLIyBoVub9mGzW zW6U@aizc3}gdaU}`oc%VF5Fge347VcehzRcmvK391#cuG5iS+sG7&BpVZR7hi10>| zVXxOX`Eiut2pEGBQ7TWY1(=Qr#%5BBbic3C=@On?a=#*_EnsEoW0m(UE7w|~+~wgdREI}V+m{&!p1BAVx#hRQdQuBH&_+fgxUc4jt4>&zaT&T|K;1A zrBCH+BT>>$-zDwHa@J0rW_yEQsC}1(+W)6e2ewJod{gEm4B1)-$SBB8kXA zx%WuTc}!|`A*(gJSgo31wRVd0X(_qTv_>>-7)`rHD(3+ylLbxN$jaWsYTbKmonW-M zimc{FG{lXTfz`lTz~+HljT@af!wBA|zRIE1QMR)K9k4%zw>K@q}AYmWG z?uXa|kkLHFISE6o)&VjKvJ+$%MC=0D2C^OGUXb-uY#B6?)nXufL3V?TgKPrX3UUv~ z$OL<~PeD$4Y&$uhBx@r*>CITwhA?jc^M*06_Ayy^7lJ*6CQab#CM_$y4uvQxF+3H` z7{w||rN{7!D%(U3xq`-Xm&bVS8aJNfQ`m2O=f3~$LN(ENbq$_ys>b-}QDs>JUak-8 zSx&ypn9?&|TLv?w&Ybl)GZ7nGZ^ESXm=7)Z4ObI8Y>%L>o7@A2fI4ywXCw?767mx- zFRPU%22;s_q2cV(Gf$R|V4{u8i3SutK>3*LqWolLi6O&<-g6e4`AK_iiu9Ae;yysy z@nt-FQ|>UfBidSh>Go~o&ujLubJkGLrfImhmjuugDr_E0uk #import "ScreenView.h" +#import "KBKeyboardView.h" -@interface ViewController : UIViewController +@interface ViewController : UIViewController @property (weak, nonatomic) IBOutlet ScreenView *screenView; +@property (nonatomic, getter=isKeyboardVisible) BOOL keyboardVisible; @end diff --git a/Mini vMac/ViewController.m b/Mini vMac/ViewController.m index 741144a..8bf6379 100644 --- a/Mini vMac/ViewController.m +++ b/Mini vMac/ViewController.m @@ -9,6 +9,8 @@ #import "ViewController.h" #import "TouchScreen.h" #import "AppDelegate.h" +#import "KBKeyboardView.h" +#import "KBKeyboardLayout.h" @interface ViewController () @@ -16,9 +18,16 @@ @implementation ViewController { + KBKeyboardView *keyboardView; + UISwipeGestureRecognizer *showKeyboardGesture, *hideKeyboardGesture; UIControl *pointingDeviceView; } +- (void)viewDidLoad { + [super viewDidLoad]; + [self installKeyboardGestures]; +} + - (BOOL)prefersStatusBarHidden { return YES; } @@ -26,6 +35,11 @@ - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self setUpPointingDevice]; + [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:@"trackpad" options:0 context:NULL]; +} + +- (void)viewDidDisappear:(BOOL)animated { + [[NSUserDefaults standardUserDefaults] removeObserver:self forKeyPath:@"trackpad"]; } - (void)setUpPointingDevice { @@ -44,4 +58,133 @@ } } +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + if (object == [NSUserDefaults standardUserDefaults]) { + if ([keyPath isEqualToString:@"keyboardLayout"] && keyboardView != nil) { + BOOL keyboardWasVisible = self.keyboardVisible; + [self setKeyboardVisible:NO animated:NO]; + [keyboardView removeFromSuperview]; + keyboardView = nil; + if (keyboardWasVisible) { + [self setKeyboardVisible:YES animated:NO]; + } + } else if ([keyPath isEqualToString:@"trackpad"]) { + [self setUpPointingDevice]; + } + } +} + +- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { + [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; + if (self.keyboardVisible) { + [self setKeyboardVisible:NO animated:NO]; + [coordinator animateAlongsideTransition:nil completion:^(id _Nonnull context) { + [self setKeyboardVisible:YES animated:YES]; + }]; + } +} + +#pragma mark - Keyboard + +- (void)installKeyboardGestures { + showKeyboardGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(showKeyboard:)]; + showKeyboardGesture.direction = UISwipeGestureRecognizerDirectionUp; + showKeyboardGesture.numberOfTouchesRequired = 2; + [self.view addGestureRecognizer:showKeyboardGesture]; + + hideKeyboardGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(hideKeyboard:)]; + hideKeyboardGesture.direction = UISwipeGestureRecognizerDirectionDown; + hideKeyboardGesture.numberOfTouchesRequired = 2; + [self.view addGestureRecognizer:hideKeyboardGesture]; +} + +- (BOOL)isKeyboardVisible { + return keyboardView != nil && CGRectIntersectsRect(keyboardView.frame, self.view.bounds) && !keyboardView.hidden; +} + +- (void)setKeyboardVisible:(BOOL)keyboardVisible { + [self setKeyboardVisible:keyboardVisible animated:YES]; +} + +- (void)showKeyboard:(id)sender { + [self setKeyboardVisible:YES animated:YES]; +} + +- (void)hideKeyboard:(id)sender { + [self setKeyboardVisible:NO animated:YES]; +} + +- (void)setKeyboardVisible:(BOOL)visible animated:(BOOL)animated { + if (self.keyboardVisible == visible) { + return; + } + + if (visible) { + [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:@"keyboardLayout" options:0 context:NULL]; + [self loadKeyboardView]; + if (keyboardView.layout == nil) { + [keyboardView removeFromSuperview]; + return; + } + [self.view addSubview:keyboardView]; + keyboardView.hidden = NO; + CGRect finalFrame = CGRectMake(0.0, self.view.bounds.size.height - keyboardView.bounds.size.height, keyboardView.bounds.size.width, keyboardView.bounds.size.height); + if (animated) { + keyboardView.frame = CGRectOffset(finalFrame, 0.0, finalFrame.size.height); + [UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{ + keyboardView.frame = finalFrame; + } completion:nil]; + } else { + keyboardView.frame = finalFrame; + } + } else { + [[NSUserDefaults standardUserDefaults] removeObserver:self forKeyPath:@"keyboardLayout"]; + if (animated) { + CGRect finalFrame = CGRectMake(0.0, self.view.bounds.size.height, keyboardView.bounds.size.width, keyboardView.bounds.size.height); + [UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{ + keyboardView.frame = finalFrame; + } completion:^(BOOL finished) { + if (finished) { + keyboardView.hidden = YES; + } + }]; + } else { + keyboardView.hidden = YES; + } + } + +} + +- (void)loadKeyboardView { + if (keyboardView != nil && keyboardView.bounds.size.width != self.view.bounds.size.width) { + // keyboard needs resizing + [keyboardView removeFromSuperview]; + keyboardView = nil; + } + + if (keyboardView == nil) { + keyboardView = [[KBKeyboardView alloc] initWithFrame:self.view.bounds]; + keyboardView.layout = [self keyboardLayout]; + keyboardView.delegate = self; + } +} + +- (KBKeyboardLayout*)keyboardLayout { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSString *layoutName = [defaults stringForKey:@"keyboardLayout"]; + NSString *layoutPath = [[NSBundle mainBundle] pathForResource:layoutName ofType:nil inDirectory:@"Keyboard Layouts"]; + if (layoutPath == nil) { + NSLog(@"Layout not found: %@", layoutPath); + } + return layoutPath ? [[KBKeyboardLayout alloc] initWithContentsOfFile:layoutPath] : nil; +} + +- (void)keyDown:(int)scancode { + [[AppDelegate sharedInstance] keyDown:scancode]; +} + +- (void)keyUp:(int)scancode { + [[AppDelegate sharedInstance] keyUp:scancode]; +} + @end