diff --git a/.gitmodules b/.gitmodules index 90c4fdd..85267ec 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "dependencies/plptools"] path = dependencies/plptools url = git@github.com:jbmorley/plptools.git +[submodule "dependencies/interact"] + path = dependencies/interact + url = https://github.com/inseven/interact.git diff --git a/PsiMac.xcodeproj/project.pbxproj b/PsiMac.xcodeproj/project.pbxproj index 16b4baf..925b097 100644 --- a/PsiMac.xcodeproj/project.pbxproj +++ b/PsiMac.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + D8184C472C253C59008FA79B /* ApplicationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8184C462C253C59008FA79B /* ApplicationModel.swift */; }; + D8184C4B2C253D73008FA79B /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8184C4A2C253D73008FA79B /* SettingsView.swift */; }; D84964DA2C1BFCB600405656 /* PsiMacApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84964D92C1BFCB600405656 /* PsiMacApp.swift */; }; D84964DC2C1BFCB600405656 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84964DB2C1BFCB600405656 /* ContentView.swift */; }; D84964DE2C1BFCB700405656 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D84964DD2C1BFCB700405656 /* Assets.xcassets */; }; @@ -45,6 +47,9 @@ D8C080782C1D7C58003128AB /* DriveListResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C080772C1D7C58003128AB /* DriveListResponse.swift */; }; D8C0807A2C1D7D8D003128AB /* PsiMacError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C080792C1D7D8D003128AB /* PsiMacError.swift */; }; D8C0807C2C1D7E79003128AB /* MachineTypeResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C0807B2C1D7E79003128AB /* MachineTypeResponse.swift */; }; + D8D3E79D2C25407E003E696D /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D3E79C2C25407E003E696D /* URL.swift */; }; + D8D3E79F2C2540D9003E696D /* Interact in Frameworks */ = {isa = PBXBuildFile; productRef = D8D3E79E2C2540D9003E696D /* Interact */; }; + D8D3E7A12C25410E003E696D /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D3E7A02C25410E003E696D /* MainMenu.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -65,6 +70,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + D8184C462C253C59008FA79B /* ApplicationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationModel.swift; sourceTree = ""; }; + D8184C4A2C253D73008FA79B /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; D84964D62C1BFCB600405656 /* PsiMac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PsiMac.app; sourceTree = BUILT_PRODUCTS_DIR; }; D84964D92C1BFCB600405656 /* PsiMacApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PsiMacApp.swift; sourceTree = ""; }; D84964DB2C1BFCB600405656 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -130,6 +137,8 @@ D8C080772C1D7C58003128AB /* DriveListResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DriveListResponse.swift; sourceTree = ""; }; D8C080792C1D7D8D003128AB /* PsiMacError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PsiMacError.swift; sourceTree = ""; }; D8C0807B2C1D7E79003128AB /* MachineTypeResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MachineTypeResponse.swift; sourceTree = ""; }; + D8D3E79C2C25407E003E696D /* URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = ""; }; + D8D3E7A02C25410E003E696D /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -139,6 +148,7 @@ files = ( D84965352C1C30E500405656 /* DataStream in Frameworks */, D84965062C1BFF1F00405656 /* Socket in Frameworks */, + D8D3E79F2C2540D9003E696D /* Interact in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -159,6 +169,25 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + D8184C482C253C6F008FA79B /* Model */ = { + isa = PBXGroup; + children = ( + D8184C462C253C59008FA79B /* ApplicationModel.swift */, + D8AD8B512C2378780063A613 /* Server.swift */, + ); + path = Model; + sourceTree = ""; + }; + D8184C492C253D62008FA79B /* Views */ = { + isa = PBXGroup; + children = ( + D84964DB2C1BFCB600405656 /* ContentView.swift */, + D8D3E7A02C25410E003E696D /* MainMenu.swift */, + D8184C4A2C253D73008FA79B /* SettingsView.swift */, + ); + path = Views; + sourceTree = ""; + }; D84964CD2C1BFCB600405656 = { isa = PBXGroup; children = ( @@ -183,21 +212,21 @@ D84964D82C1BFCB600405656 /* PsiMac */ = { isa = PBXGroup; children = ( - D8AD8B532C2379A10063A613 /* Info.plist */, D84964E22C1BFCB700405656 /* PsiMac.entitlements */, D876E6142C22C2F6004A6881 /* PsiMacRelease.entitlements */, D876E5D72C22B623004A6881 /* PsiMac-Bridging-Header.h */, - D84964DB2C1BFCB600405656 /* ContentView.swift */, + D8AD8B532C2379A10063A613 /* Info.plist */, D84965392C1D077100405656 /* MachineType.swift */, D84964D92C1BFCB600405656 /* PsiMacApp.swift */, D8C080792C1D7D8D003128AB /* PsiMacError.swift */, - D8AD8B512C2378780063A613 /* Server.swift */, D84964DD2C1BFCB700405656 /* Assets.xcassets */, D849653D2C1D07AD00405656 /* Extensions */, D84965362C1D06CA00405656 /* Messages */, + D8184C482C253C6F008FA79B /* Model */, D8AD8B502C2377F60063A613 /* plptools */, D84964DF2C1BFCB700405656 /* Preview Content */, D8C080712C1D7C07003128AB /* Servers */, + D8184C492C253D62008FA79B /* Views */, ); path = PsiMac; sourceTree = ""; @@ -250,9 +279,10 @@ D849653D2C1D07AD00405656 /* Extensions */ = { isa = PBXGroup; children = ( - D849653E2C1D07B500405656 /* Socket.swift */, D84965422C1D08AB00405656 /* DataReadStream.swift */, D84965442C1D08C900405656 /* Packetable.swift */, + D849653E2C1D07B500405656 /* Socket.swift */, + D8D3E79C2C25407E003E696D /* URL.swift */, ); path = Extensions; sourceTree = ""; @@ -345,6 +375,7 @@ packageProductDependencies = ( D84965052C1BFF1F00405656 /* Socket */, D84965342C1C30E500405656 /* DataStream */, + D8D3E79E2C2540D9003E696D /* Interact */, ); productName = PsiMac; productReference = D84964D62C1BFCB600405656 /* PsiMac.app */; @@ -422,6 +453,7 @@ packageReferences = ( D84965042C1BFF1F00405656 /* XCRemoteSwiftPackageReference "BlueSocket" */, D84965322C1C30DB00405656 /* XCLocalSwiftPackageReference "dependencies/DataStream" */, + D8D3E79B2C25405A003E696D /* XCLocalSwiftPackageReference "dependencies/interact" */, ); productRefGroup = D84964D72C1BFCB600405656 /* Products */; projectDirPath = ""; @@ -472,6 +504,7 @@ D84965382C1D075400405656 /* MachineInfoResponse.swift in Sources */, D84965452C1D08C900405656 /* Packetable.swift in Sources */, D8C080702C1D7BE1003128AB /* OwnerInfoResponse.swift in Sources */, + D8D3E79D2C25407E003E696D /* URL.swift in Sources */, D8C0807A2C1D7D8D003128AB /* PsiMacError.swift in Sources */, D876E6022C22B7D5004A6881 /* linkchan.cc in Sources */, D8C080762C1D7C43003128AB /* RPCS.swift in Sources */, @@ -486,12 +519,15 @@ D876E60B2C22B88A004A6881 /* log.cc in Sources */, D876E5F42C22B7C8004A6881 /* channel.cc in Sources */, D849653A2C1D077100405656 /* MachineType.swift in Sources */, + D8184C4B2C253D73008FA79B /* SettingsView.swift in Sources */, D8C0807C2C1D7E79003128AB /* MachineTypeResponse.swift in Sources */, + D8184C472C253C59008FA79B /* ApplicationModel.swift in Sources */, D876E5F52C22B7C8004A6881 /* link.cc in Sources */, D849653F2C1D07B500405656 /* Socket.swift in Sources */, D8C080782C1D7C58003128AB /* DriveListResponse.swift in Sources */, D876E6122C22BD96004A6881 /* main.cc in Sources */, D876E5E82C22B76B004A6881 /* bufferstore.cc in Sources */, + D8D3E7A12C25410E003E696D /* MainMenu.swift in Sources */, D876E6042C22B7D5004A6881 /* packet.cc in Sources */, D876E5EE2C22B78B004A6881 /* iowatch.cc in Sources */, D84964DC2C1BFCB600405656 /* ContentView.swift in Sources */, @@ -837,6 +873,10 @@ isa = XCLocalSwiftPackageReference; relativePath = dependencies/DataStream; }; + D8D3E79B2C25405A003E696D /* XCLocalSwiftPackageReference "dependencies/interact" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = dependencies/interact; + }; /* End XCLocalSwiftPackageReference section */ /* Begin XCRemoteSwiftPackageReference section */ @@ -861,6 +901,11 @@ package = D84965322C1C30DB00405656 /* XCLocalSwiftPackageReference "dependencies/DataStream" */; productName = DataStream; }; + D8D3E79E2C2540D9003E696D /* Interact */ = { + isa = XCSwiftPackageProductDependency; + package = D8D3E79B2C25405A003E696D /* XCLocalSwiftPackageReference "dependencies/interact" */; + productName = Interact; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = D84964CE2C1BFCB600405656 /* Project object */; diff --git a/PsiMac/Extensions/URL.swift b/PsiMac/Extensions/URL.swift new file mode 100644 index 0000000..f101d0d --- /dev/null +++ b/PsiMac/Extensions/URL.swift @@ -0,0 +1,25 @@ +// PsiMac -- Psion connectivity for macOS +// +// Copyright (C) 2024 Jason Morley +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +import Foundation + +extension URL { + + static let settings = URL(string: "x-reconnect://settings")! + +} diff --git a/PsiMac/Info.plist b/PsiMac/Info.plist index bc11256..be9f1b7 100644 --- a/PsiMac/Info.plist +++ b/PsiMac/Info.plist @@ -2,6 +2,19 @@ + CFBundleURLTypes + + + CFBundleTypeRole + Viewer + CFBundleURLName + uk.co.jbmorley.reconnect.types.url + CFBundleURLSchemes + + x-reconnect + + + ITSAppUsesNonExemptEncryption diff --git a/PsiMac/Model/ApplicationModel.swift b/PsiMac/Model/ApplicationModel.swift new file mode 100644 index 0000000..3751a57 --- /dev/null +++ b/PsiMac/Model/ApplicationModel.swift @@ -0,0 +1,42 @@ +// PsiMac -- Psion connectivity for macOS +// +// Copyright (C) 2024 Jason Morley +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +import SwiftUI + +import Interact + +@Observable +class ApplicationModel { + + enum SettingsKey: String { + case device + } + + @MainActor var device: String { + didSet { + keyedDefaults.set(device, forKey: .device) + } + } + + private let keyedDefaults = KeyedDefaults() + + @MainActor init() { + device = keyedDefaults.string(forKey: .device, default: "") + } + +} diff --git a/PsiMac/Server.swift b/PsiMac/Model/Server.swift similarity index 94% rename from PsiMac/Server.swift rename to PsiMac/Model/Server.swift index 4f9b46c..b22df41 100644 --- a/PsiMac/Server.swift +++ b/PsiMac/Model/Server.swift @@ -37,8 +37,8 @@ class Server { } } - let device = "/dev/tty.usbserial-AL00AYCG" -// let device = "/dev/tty.usbserial-A91MGK6M" +// let device = "/dev/tty.usbserial-AL00AYCG" + let device = "/dev/tty.usbserial-A91MGK6M" // let log: UInt16 = 1 | 2 | 4 | 8 | 18 | 32 | 64 let log: UInt16 = 0 diff --git a/PsiMac/PsiMacApp.swift b/PsiMac/PsiMacApp.swift index 41c0087..ce595d2 100644 --- a/PsiMac/PsiMacApp.swift +++ b/PsiMac/PsiMacApp.swift @@ -18,17 +18,22 @@ import SwiftUI +import Interact + @main struct PsiMacApp: App { @State var server = Server() + @State var applicationModel: ApplicationModel + + init() { + self.applicationModel = ApplicationModel() + } var body: some Scene { MenuBarExtra { - Button("Connect") { - - } + MainMenu() } label: { if server.isConnected { Image("Connected") @@ -37,5 +42,12 @@ struct PsiMacApp: App { } } + Window("Settings", id: "settings") { + SettingsView() + } + .environment(applicationModel) + .handlesExternalEvents(matching: [.settings]) + } + } diff --git a/PsiMac/ContentView.swift b/PsiMac/Views/ContentView.swift similarity index 100% rename from PsiMac/ContentView.swift rename to PsiMac/Views/ContentView.swift diff --git a/PsiMac/Views/MainMenu.swift b/PsiMac/Views/MainMenu.swift new file mode 100644 index 0000000..35deb9a --- /dev/null +++ b/PsiMac/Views/MainMenu.swift @@ -0,0 +1,31 @@ +// PsiMac -- Psion connectivity for macOS +// +// Copyright (C) 2024 Jason Morley +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +import SwiftUI + +struct MainMenu: View { + + @Environment(\.openURL) private var openURL + + var body: some View { + Button("Settings") { + openURL(.settings) + } + } + +} diff --git a/PsiMac/Views/SettingsView.swift b/PsiMac/Views/SettingsView.swift new file mode 100644 index 0000000..90e2fe4 --- /dev/null +++ b/PsiMac/Views/SettingsView.swift @@ -0,0 +1,33 @@ +// PsiMac -- Psion connectivity for macOS +// +// Copyright (C) 2024 Jason Morley +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +import SwiftUI + +struct SettingsView: View { + + @Environment(ApplicationModel.self) var applicationModel + + var body: some View { + @Bindable var applicationModel = applicationModel + Form { + TextField("Serial Device", text: $applicationModel.device) + } + .formStyle(.grouped) + } + +} diff --git a/dependencies/interact b/dependencies/interact new file mode 160000 index 0000000..42d74d9 --- /dev/null +++ b/dependencies/interact @@ -0,0 +1 @@ +Subproject commit 42d74d95360c6859e3fc1508f5ab734d054e27bf