├── .tool-versions ├── .github └── FUNDING.yml ├── docs ├── docs │ ├── development │ │ ├── index.md │ │ ├── assets │ │ │ └── images │ │ │ │ ├── back.gif │ │ │ │ ├── cnt.gif │ │ │ │ ├── conv.gif │ │ │ │ ├── rest.gif │ │ │ │ ├── sync.gif │ │ │ │ ├── back@2x.gif │ │ │ │ ├── cnt@2x.gif │ │ │ │ ├── conv@2x.gif │ │ │ │ ├── install.gif │ │ │ │ ├── rest@2x.gif │ │ │ │ ├── sync@2x.gif │ │ │ │ └── install@2x.gif │ │ ├── debugging │ │ │ └── index.md │ │ └── architecture │ │ │ └── index.md │ ├── index.md │ ├── glossary │ │ └── index.md │ └── getting-started │ │ └── index.md ├── images │ ├── icons │ │ ├── drive.png │ │ ├── psion.png │ │ ├── install.png │ │ ├── preview.png │ │ └── sketch.png │ ├── icon_128x128.png │ ├── icon_128x128@2x.png │ ├── screenshot-default@2x.png │ └── screenshot-default-dark@2x.png ├── 404.md ├── privacy-policy │ └── index.md ├── assets │ └── css │ │ └── custom.css ├── Gemfile ├── index.md ├── license │ └── index.md └── _config.yml ├── images └── screenshot@2x.png ├── tools ├── ReconnectTools.sis └── reconnect-tools │ ├── screenshot.exe │ └── Reconnect Tools.pkg ├── Reconnect ├── Resources │ ├── cnt.gif │ ├── cnt@2x.gif │ ├── SYS$RPCS.IMG │ ├── install.gif │ └── install@2x.gif ├── Assets.xcassets │ ├── Contents.json │ ├── Data16.imageset │ │ ├── data.png │ │ └── Contents.json │ ├── OPL16.imageset │ │ ├── opl.png │ │ └── Contents.json │ ├── Word16.imageset │ │ ├── word.png │ │ └── Contents.json │ ├── Disk16.imageset │ │ ├── Disk32.png │ │ └── Contents.json │ ├── Sheet16.imageset │ │ ├── sheet.png │ │ └── Contents.json │ ├── Agenda16.imageset │ │ ├── agenda.png │ │ └── Contents.json │ ├── Drive16.imageset │ │ ├── Drive32.png │ │ └── Contents.json │ ├── Jotter16.imageset │ │ ├── jotter.png │ │ └── Contents.json │ ├── Psion16.imageset │ │ ├── Psion16.png │ │ └── Contents.json │ ├── Record16.imageset │ │ ├── record.png │ │ └── Contents.json │ ├── Sketch16.imageset │ │ ├── sketch.png │ │ └── Contents.json │ ├── Folder16.imageset │ │ ├── Folder32.png │ │ └── Contents.json │ ├── Icon.imageset │ │ ├── icon_512x512.png │ │ ├── icon_512x512@2x@2x.png │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── icon_16x16.png │ │ ├── icon_32x32.png │ │ ├── icon_128x128.png │ │ ├── icon_256x256.png │ │ ├── icon_512x512.png │ │ ├── icon_16x16@2x@2x.png │ │ ├── icon_32x32@2x@2x.png │ │ ├── icon_128x128@2x@2x.png │ │ ├── icon_256x256@2x@2x.png │ │ ├── icon_512x512@2x@2x.png │ │ └── Contents.json │ ├── Install16.imageset │ │ ├── Install16.png │ │ └── Contents.json │ ├── Installer.imageset │ │ ├── installer.png │ │ ├── installer@2x.png │ │ └── Contents.json │ ├── FileUnknown16.imageset │ │ ├── Unknown32.png │ │ └── Contents.json │ ├── UnknownAppIcon.imageset │ │ ├── question.png │ │ └── Contents.json │ ├── Disconnected.imageset │ │ ├── disconnected32.png │ │ ├── disconnected@2x.png │ │ └── Contents.json │ ├── Disconnected16.imageset │ │ ├── Disconnected16.png │ │ └── Contents.json │ └── AccentColor.colorset │ │ └── Contents.json ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── Reconnect.entitlements ├── Model │ ├── SidebarItem.swift │ ├── Refreshable.swift │ ├── ParentNavigable.swift │ ├── CheckForUpdatesViewModel.swift │ ├── FileReference.swift │ ├── FileManageable.swift │ ├── ParentNavigableProxy.swift │ ├── RefreshableProxy.swift │ ├── EditableTextModel.swift │ └── BrowserSection.swift ├── Extensions │ ├── ShapeStyle.swift │ └── SisInstallError.swift ├── Views │ ├── DeviceModelProxy.swift │ ├── SectionLabel.swift │ ├── AnimagedImage.swift │ ├── ProgressAnimation.swift │ ├── DeviceDriveGroup.swift │ ├── PixelImage.swift │ ├── EditableText.swift │ ├── DeviceView.swift │ ├── ThumbnailView.swift │ ├── CheckForUpdatesView.swift │ ├── FileTypePopover.swift │ ├── Settings │ │ ├── SettingsButton.swift │ │ └── SerialDeviceEnableToggle.swift │ ├── DetailsGroup.swift │ └── Installer │ │ ├── ReplaceQueryInstallerPage.swift │ │ └── TextQueryInstallerPage.swift ├── Toolbars │ ├── ToolbarSpacer.swift │ ├── DeviceToolbar.swift │ ├── RefreshToolbar.swift │ └── FileToolbar.swift ├── Commands │ ├── SparkleCommands.swift │ ├── RefreshCommands.swift │ ├── DeviceCommands.swift │ └── NavigationCommands.swift ├── Environment │ └── WindowProxy.swift ├── Software Index │ ├── API │ │ ├── Kind.swift │ │ ├── ReferenceItem.swift │ │ ├── Image.swift │ │ ├── Version.swift │ │ ├── Collection.swift │ │ ├── Program.swift │ │ └── Release.swift │ ├── Model │ │ └── PsionSoftwareIndexError.swift │ ├── Views │ │ ├── IconView.swift │ │ └── ItemView.swift │ └── Styles │ │ └── UnadornedCircularProgressViewStyle.swift ├── Styles │ └── DetailsLabeledContentStyle.swift ├── Windows │ ├── TransfersWindow.swift │ └── NSInstallerWindow.swift ├── AppDelegate.swift └── Modifiers │ ├── OptionalNavigationSubtitle.swift │ └── ShowsDeviceProgress.swift ├── graphics ├── assets │ ├── opl │ │ └── opl.png │ ├── data │ │ └── data.png │ ├── word │ │ └── word.png │ ├── disk │ │ └── Disk32.png │ ├── sheet │ │ └── sheet.png │ ├── agenda │ │ └── agenda.png │ ├── drives │ │ └── Drive32.png │ ├── folder │ │ ├── Folder32.png │ │ ├── Directory16.png │ │ ├── Directory24.png │ │ ├── Directory32.acorn │ │ └── Directory32.png │ ├── jotter │ │ └── jotter.png │ ├── record │ │ └── record.png │ ├── sketch │ │ └── sketch.png │ ├── series-5 │ │ ├── series5.png │ │ └── series5large.png │ ├── unknown │ │ ├── Unknown32.png │ │ ├── Unknown32.acorn │ │ └── Unknown32_PsiWin.png │ ├── status │ │ ├── StatusConnected.png │ │ ├── StatusDisconnected.png │ │ ├── StatusConnectedDark.png │ │ └── StatusDisconnectedDark.png │ └── disconnected │ │ ├── disconnected32.png │ │ └── disconnected32@2x.png └── app-icon │ ├── Series 5 Icon.sketch │ └── SF Symbols Icon.symbolic ├── ReconnectMenu ├── Assets.xcassets │ ├── Contents.json │ ├── AppIcon.appiconset │ │ ├── icon_128x128.png │ │ ├── icon_16x16.png │ │ ├── icon_256x256.png │ │ ├── icon_32x32.png │ │ ├── icon_512x512.png │ │ ├── icon_16x16@2x@2x.png │ │ ├── icon_32x32@2x@2x.png │ │ ├── icon_128x128@2x@2x.png │ │ ├── icon_256x256@2x@2x.png │ │ ├── icon_512x512@2x@2x.png │ │ └── Contents.json │ ├── StatusUnknown.imageset │ │ ├── StatusMissing.png │ │ └── Contents.json │ ├── StatusConnected.imageset │ │ ├── StatusConnected.png │ │ ├── StatusConnectedDark.png │ │ └── Contents.json │ ├── StatusDisconnected.imageset │ │ ├── StatusDisconnected.png │ │ ├── StatusDisconnectedDark.png │ │ └── Contents.json │ └── AccentColor.colorset │ │ └── Contents.json ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── Info.plist ├── ReconnectMenu.entitlements ├── ReconnectMenuApp.swift └── Views │ ├── StatusIcon.swift │ └── MainMenu.swift ├── .gitignore ├── profiles ├── Reconnect_Developer_ID_Profile.provisionprofile ├── Reconnect_Menu_Developer_ID_Profile.provisionprofile └── Reconnect_Previews_Developer_ID_Profile.provisionprofile ├── ReconnectCore ├── .gitignore ├── Tests │ └── ReconnectCoreTests │ │ └── ReconnectCoreTests.swift ├── Sources │ └── ReconnectCore │ │ ├── Extensions │ │ ├── Array.swift │ │ ├── UTType.swift │ │ ├── Sis.File.swift │ │ ├── rfsv.errs.swift │ │ ├── NSRunningApplication.swift │ │ ├── UInt32.swift │ │ ├── DriveInfo.swift │ │ ├── MachineType.swift │ │ ├── Locale.swift │ │ └── PsiLuaEnv.swift │ │ ├── Utilities │ │ └── Graphics.swift │ │ ├── XPC │ │ ├── DaemonClientInterface.swift │ │ ├── DaemonInterface.swift │ │ └── DaemonInfo.swift │ │ ├── PLP │ │ └── PsionClient.swift │ │ └── Model │ │ └── ReconnectError.swift └── Package.swift ├── scripts ├── release-notes.html ├── release-notes.md ├── release.sh ├── environment.sh ├── update-release-notes.sh ├── build-website.sh └── install-dependencies.sh ├── ReconnectPreviews ├── ReconnectPreviews.entitlements ├── Info.plist ├── Base.lproj │ └── PreviewViewController.xib ├── InstallerPreviewView.swift └── PreviewViewController.swift ├── reconnectd ├── uk.co.jbmorley.reconnect.apps.apple.reconnectd.plist ├── Info.plist └── main.swift ├── ExportOptions.plist ├── .gitmodules ├── ReconnectTests ├── ReconnectTests.swift └── WindowsPathTests.swift ├── README.md └── ReconnectUITests ├── ReconnectUITestsLaunchTests.swift └── ReconnectUITests.swift /.tool-versions: -------------------------------------------------------------------------------- 1 | python 3.12.1 2 | ruby 3.1.2 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [jbmorley] 2 | buy_me_a_coffee: jbmorley 3 | -------------------------------------------------------------------------------- /docs/docs/development/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Development 3 | --- 4 | -------------------------------------------------------------------------------- /images/screenshot@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/images/screenshot@2x.png -------------------------------------------------------------------------------- /tools/ReconnectTools.sis: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/tools/ReconnectTools.sis -------------------------------------------------------------------------------- /Reconnect/Resources/cnt.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Resources/cnt.gif -------------------------------------------------------------------------------- /docs/images/icons/drive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/images/icons/drive.png -------------------------------------------------------------------------------- /docs/images/icons/psion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/images/icons/psion.png -------------------------------------------------------------------------------- /graphics/assets/opl/opl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/opl/opl.png -------------------------------------------------------------------------------- /Reconnect/Resources/cnt@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Resources/cnt@2x.gif -------------------------------------------------------------------------------- /docs/images/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/images/icon_128x128.png -------------------------------------------------------------------------------- /docs/images/icons/install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/images/icons/install.png -------------------------------------------------------------------------------- /docs/images/icons/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/images/icons/preview.png -------------------------------------------------------------------------------- /docs/images/icons/sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/images/icons/sketch.png -------------------------------------------------------------------------------- /graphics/assets/data/data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/data/data.png -------------------------------------------------------------------------------- /graphics/assets/word/word.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/word/word.png -------------------------------------------------------------------------------- /Reconnect/Resources/SYS$RPCS.IMG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Resources/SYS$RPCS.IMG -------------------------------------------------------------------------------- /Reconnect/Resources/install.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Resources/install.gif -------------------------------------------------------------------------------- /docs/images/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/images/icon_128x128@2x.png -------------------------------------------------------------------------------- /graphics/assets/disk/Disk32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/disk/Disk32.png -------------------------------------------------------------------------------- /graphics/assets/sheet/sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/sheet/sheet.png -------------------------------------------------------------------------------- /Reconnect/Resources/install@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Resources/install@2x.gif -------------------------------------------------------------------------------- /graphics/assets/agenda/agenda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/agenda/agenda.png -------------------------------------------------------------------------------- /graphics/assets/drives/Drive32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/drives/Drive32.png -------------------------------------------------------------------------------- /graphics/assets/folder/Folder32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/folder/Folder32.png -------------------------------------------------------------------------------- /graphics/assets/jotter/jotter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/jotter/jotter.png -------------------------------------------------------------------------------- /graphics/assets/record/record.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/record/record.png -------------------------------------------------------------------------------- /graphics/assets/sketch/sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/sketch/sketch.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /docs/images/screenshot-default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/images/screenshot-default@2x.png -------------------------------------------------------------------------------- /graphics/assets/series-5/series5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/series-5/series5.png -------------------------------------------------------------------------------- /graphics/assets/unknown/Unknown32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/unknown/Unknown32.png -------------------------------------------------------------------------------- /tools/reconnect-tools/screenshot.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/tools/reconnect-tools/screenshot.exe -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /graphics/app-icon/Series 5 Icon.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/app-icon/Series 5 Icon.sketch -------------------------------------------------------------------------------- /graphics/assets/folder/Directory16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/folder/Directory16.png -------------------------------------------------------------------------------- /graphics/assets/folder/Directory24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/folder/Directory24.png -------------------------------------------------------------------------------- /graphics/assets/folder/Directory32.acorn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/folder/Directory32.acorn -------------------------------------------------------------------------------- /graphics/assets/folder/Directory32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/folder/Directory32.png -------------------------------------------------------------------------------- /graphics/assets/unknown/Unknown32.acorn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/unknown/Unknown32.acorn -------------------------------------------------------------------------------- /docs/images/screenshot-default-dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/images/screenshot-default-dark@2x.png -------------------------------------------------------------------------------- /graphics/assets/series-5/series5large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/series-5/series5large.png -------------------------------------------------------------------------------- /graphics/assets/status/StatusConnected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/status/StatusConnected.png -------------------------------------------------------------------------------- /docs/404.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Not Found 3 | layout: page 4 | --- 5 | 6 |
The page you requested could not be found.
7 | -------------------------------------------------------------------------------- /docs/docs/development/assets/images/back.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/back.gif -------------------------------------------------------------------------------- /docs/docs/development/assets/images/cnt.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/cnt.gif -------------------------------------------------------------------------------- /docs/docs/development/assets/images/conv.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/conv.gif -------------------------------------------------------------------------------- /docs/docs/development/assets/images/rest.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/rest.gif -------------------------------------------------------------------------------- /docs/docs/development/assets/images/sync.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/sync.gif -------------------------------------------------------------------------------- /graphics/assets/status/StatusDisconnected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/status/StatusDisconnected.png -------------------------------------------------------------------------------- /graphics/assets/unknown/Unknown32_PsiWin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/unknown/Unknown32_PsiWin.png -------------------------------------------------------------------------------- /docs/docs/development/assets/images/back@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/back@2x.gif -------------------------------------------------------------------------------- /docs/docs/development/assets/images/cnt@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/cnt@2x.gif -------------------------------------------------------------------------------- /docs/docs/development/assets/images/conv@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/conv@2x.gif -------------------------------------------------------------------------------- /docs/docs/development/assets/images/install.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/install.gif -------------------------------------------------------------------------------- /docs/docs/development/assets/images/rest@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/rest@2x.gif -------------------------------------------------------------------------------- /docs/docs/development/assets/images/sync@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/sync@2x.gif -------------------------------------------------------------------------------- /graphics/assets/disconnected/disconnected32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/disconnected/disconnected32.png -------------------------------------------------------------------------------- /graphics/assets/status/StatusConnectedDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/status/StatusConnectedDark.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .build 2 | .swiftpm 3 | /_site 4 | /.local 5 | /archives 6 | /build 7 | /docs/_site 8 | /docs/.jekyll-cache 9 | /docs/releases 10 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Data16.imageset/data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Data16.imageset/data.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/OPL16.imageset/opl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/OPL16.imageset/opl.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Word16.imageset/word.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Word16.imageset/word.png -------------------------------------------------------------------------------- /Reconnect/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /docs/docs/development/assets/images/install@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/docs/docs/development/assets/images/install@2x.gif -------------------------------------------------------------------------------- /graphics/assets/disconnected/disconnected32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/disconnected/disconnected32@2x.png -------------------------------------------------------------------------------- /graphics/assets/status/StatusDisconnectedDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/graphics/assets/status/StatusDisconnectedDark.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Disk16.imageset/Disk32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Disk16.imageset/Disk32.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Sheet16.imageset/sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Sheet16.imageset/sheet.png -------------------------------------------------------------------------------- /ReconnectMenu/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Agenda16.imageset/agenda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Agenda16.imageset/agenda.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Drive16.imageset/Drive32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Drive16.imageset/Drive32.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Jotter16.imageset/jotter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Jotter16.imageset/jotter.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Psion16.imageset/Psion16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Psion16.imageset/Psion16.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Record16.imageset/record.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Record16.imageset/record.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Sketch16.imageset/sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Sketch16.imageset/sketch.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Folder16.imageset/Folder32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Folder16.imageset/Folder32.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Icon.imageset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Icon.imageset/icon_512x512.png -------------------------------------------------------------------------------- /profiles/Reconnect_Developer_ID_Profile.provisionprofile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/profiles/Reconnect_Developer_ID_Profile.provisionprofile -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_16x16.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_32x32.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Install16.imageset/Install16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Install16.imageset/Install16.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Installer.imageset/installer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Installer.imageset/installer.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_128x128.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_256x256.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_512x512.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/FileUnknown16.imageset/Unknown32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/FileUnknown16.imageset/Unknown32.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Icon.imageset/icon_512x512@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Icon.imageset/icon_512x512@2x@2x.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Installer.imageset/installer@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Installer.imageset/installer@2x.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/UnknownAppIcon.imageset/question.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/UnknownAppIcon.imageset/question.png -------------------------------------------------------------------------------- /profiles/Reconnect_Menu_Developer_ID_Profile.provisionprofile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/profiles/Reconnect_Menu_Developer_ID_Profile.provisionprofile -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x@2x.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x@2x.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_128x128.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_16x16.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_256x256.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_32x32.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_512x512.png -------------------------------------------------------------------------------- /profiles/Reconnect_Previews_Developer_ID_Profile.provisionprofile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/profiles/Reconnect_Previews_Developer_ID_Profile.provisionprofile -------------------------------------------------------------------------------- /tools/reconnect-tools/Reconnect Tools.pkg: -------------------------------------------------------------------------------- 1 | &EN 2 | #{"Reconnect Tools"},(0x100092cb),0,1,0 3 | "D:\Projects\reconnect-tools\screenshot.exe"-"C:\System\Reconnect\screenshot.exe" 4 | -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x@2x.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x@2x.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x@2x.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Disconnected.imageset/disconnected32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Disconnected.imageset/disconnected32.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Disconnected.imageset/disconnected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Disconnected.imageset/disconnected@2x.png -------------------------------------------------------------------------------- /Reconnect/Assets.xcassets/Disconnected16.imageset/Disconnected16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/Reconnect/Assets.xcassets/Disconnected16.imageset/Disconnected16.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x@2x.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x@2x.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/StatusUnknown.imageset/StatusMissing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/StatusUnknown.imageset/StatusMissing.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x@2x.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x@2x.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x@2x.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/StatusConnected.imageset/StatusConnected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/StatusConnected.imageset/StatusConnected.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/StatusConnected.imageset/StatusConnectedDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/StatusConnected.imageset/StatusConnectedDark.png -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/StatusDisconnected.imageset/StatusDisconnected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/StatusDisconnected.imageset/StatusDisconnected.png -------------------------------------------------------------------------------- /ReconnectCore/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /ReconnectMenu/Assets.xcassets/StatusDisconnected.imageset/StatusDisconnectedDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inseven/reconnect/HEAD/ReconnectMenu/Assets.xcassets/StatusDisconnected.imageset/StatusDisconnectedDark.png -------------------------------------------------------------------------------- /ReconnectMenu/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 |![]()
Manage Files
9 |Browse files using a familiar interface and transfer to and from your Psion.
10 |![]()
Convert Documents
14 |Automatically convert Sketch and MBM files.
15 |![]()
Install Programs
19 |Add and remove programs.
20 |![]()
Preview Installers
24 |Reconnect provides a QuickLook plugin allowing you to quickly preview SIS files.
25 |![]()
Capture Screenshots
29 |Install Reconnect Tools on your Psion and capture screenshots directly from your Mac.
30 |
8 |
9 | ## License
10 |
11 | Reconnect is licensed under the GNU General Public License (GPL) version 2 (see [LICENSE](LICENSE)). It depends on the following separately licensed third-party libraries and components:
12 |
13 | - [Diligence](https://github.com/inseven/diligence), MIT License
14 | - [Interact](https://github.com/inseven/interact), MIT License
15 | - [Licensable](https://github.com/inseven/licensable), MIT License
16 | - [Lua](https://www.lua.org), MIT License
17 | - [OpoLua](https://github.com/inseven/opolua), MIT License
18 | - [plptools](https://github.com/rrthomas/plptools), GPL 2.0 License
19 | - [PsionSoftwareIndexSwift](https://github.com/inseven/PsionSoftwareIndexSwift), MIT License
20 | - [Sparkle](https://github.com/sparkle-project/Sparkle), Sparkle License
21 | - [Swift Algorithms](https://github.com/apple/swift-algorithms), Apache 2.0 License
22 | - [Swift Argument Parser](https://github.com/apple/swift-argument-parser), Apache 2.0 License
23 | - [Swift Numerics](https://github.com/apple/swift-numerics), Apache 2.0 License
24 |
25 | Reconnect includes graphics (icons and animations) from the original Psion PsiWin and PsiMac software. These remain copyright Psion PLC.
26 |
--------------------------------------------------------------------------------
/Reconnect/Software Index/API/Collection.swift:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2024-2025 Jason Morley
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in all
11 | // copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | // SOFTWARE.
20 |
21 | import Foundation
22 |
23 | struct Collection: Codable, Identifiable, Hashable {
24 |
25 | var id: String {
26 | return identifier
27 | }
28 |
29 | let identifier: String
30 | let items: [Release]
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/Reconnect/Extensions/SisInstallError.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import Foundation
20 |
21 | import ReconnectCore
22 | import OpoLua
23 |
24 | extension Sis.InstallError: @retroactive LocalizedError {
25 |
26 | public var errorDescription: String? {
27 | switch self {
28 | case .userCancelled:
29 | return "User cancelled"
30 | case .epocError(let code, _):
31 | return LocalizedEpoc32ErrorCode(code)
32 | case .internalError(let message):
33 | return message
34 | case .isStub:
35 | return "Invalid SIS file"
36 | }
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/scripts/environment.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Reconnect -- Psion connectivity for macOS
4 | #
5 | # Copyright (C) 2024-2025 Jason Morley
6 | #
7 | # This program is free software; you can redistribute it and/or modify
8 | # it under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation; either version 2 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program; if not, write to the Free Software
19 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 |
21 | ROOT_DIRECTORY="$( cd "$( dirname "$( dirname "${BASH_SOURCE[0]}" )" )" &> /dev/null && pwd )"
22 | SCRIPTS_DIRECTORY="$ROOT_DIRECTORY/scripts"
23 |
24 | export LOCAL_TOOLS_PATH="$ROOT_DIRECTORY/.local"
25 |
26 | export BIN_DIRECTORY="$ROOT_DIRECTORY/.local/bin"
27 | export PATH=$BIN_DIRECTORY:$PATH
28 |
29 | source "$LOCAL_TOOLS_PATH/python/bin/activate"
30 |
31 | export PIPENV_VENV_IN_PROJECT=1
32 | export PIPENV_IGNORE_VIRTUALENVS=1
33 |
34 | export PATH=$PATH:"$SCRIPTS_DIRECTORY/changes"
35 | export PATH=$PATH:"$SCRIPTS_DIRECTORY/build-tools"
36 | export PATH=$PATH:"$ROOT_DIRECTORY/dependencies/diligence/scripts"
37 |
--------------------------------------------------------------------------------
/ReconnectMenu/Views/StatusIcon.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 |
21 | import ReconnectCore
22 |
23 | struct StatusIcon: View {
24 |
25 | @Environment(ApplicationModel.self) var applicationModel
26 |
27 | var body: some View {
28 | if !applicationModel.isDaemonConnected {
29 | Image("StatusUnknown")
30 | .interpolation(.none)
31 |
32 | } else if applicationModel.isDeviceConnected {
33 | Image("StatusConnected")
34 | .interpolation(.none)
35 | } else {
36 | Image("StatusDisconnected")
37 | .interpolation(.none)
38 | }
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/Reconnect/Windows/TransfersWindow.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 |
21 | import Interact
22 |
23 | struct TransfersWindow: Scene {
24 |
25 | static func reveal() {
26 | DispatchQueue.main.async {
27 | NSWorkspace.shared.open(.transfers)
28 | }
29 | }
30 |
31 | static let id = "transfers"
32 |
33 | @Environment(TransfersModel.self) private var transfersModel
34 |
35 | var body: some Scene {
36 | Window("Transfers", id: Self.id) {
37 | TransfersView(transfersModel: transfersModel)
38 | }
39 | .windowResizability(.contentSize)
40 | .handlesExternalEvents(matching: [.transfers])
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/ReconnectCore/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.10
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "ReconnectCore",
8 | platforms: [
9 | .macOS(.v14),
10 | ],
11 | products: [
12 | // Products define the executables and libraries a package produces, making them visible to other packages.
13 | .library(
14 | name: "ReconnectCore",
15 | targets: ["ReconnectCore"]),
16 | ],
17 | dependencies: [
18 | .package(path: "../dependencies/diligence"),
19 | .package(path: "../dependencies/opolua"),
20 | .package(path: "../dependencies/plptools"),
21 | ],
22 | targets: [
23 | // Targets are the basic building blocks of a package, defining a module or a test suite.
24 | // Targets can depend on other targets in this package and products from dependencies.
25 | .target(
26 | name: "ReconnectCore",
27 | dependencies: [
28 | .product(name: "Diligence", package: "diligence"),
29 | .product(name: "OpoLua", package: "opolua"),
30 | .product(name: "ncp", package: "plptools"),
31 | ],
32 | swiftSettings: [
33 | .interoperabilityMode(.Cxx)
34 | ]
35 | ),
36 | .testTarget(
37 | name: "ReconnectCoreTests",
38 | dependencies: ["ReconnectCore"]),
39 | ]
40 | )
41 |
--------------------------------------------------------------------------------
/Reconnect/Commands/RefreshCommands.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 |
21 | public struct RefreshCommands: Commands {
22 |
23 | @FocusedObject private var refreshableProxy: RefreshableProxy?
24 |
25 | init() {
26 | }
27 |
28 | public var body: some Commands {
29 |
30 | CommandGroup(before: .newItem) {
31 |
32 | Button {
33 | refreshableProxy?.refresh()
34 | } label: {
35 | Label("Refresh", systemImage: "arrow.clockwise")
36 | }
37 | .keyboardShortcut("R")
38 | .disabled(!(refreshableProxy?.canRefresh ?? false))
39 |
40 | Divider()
41 | }
42 |
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/Reconnect/Model/ParentNavigableProxy.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 |
21 | class ParentNavigableProxy: ObservableObject, ParentNavigable {
22 |
23 | var canNavigateToParent: Bool {
24 | return _canNavigateToParent()
25 | }
26 |
27 | let _canNavigateToParent: @MainActor () -> Bool
28 | let _navigateToParent: @MainActor () -> Void
29 |
30 | init(_ parentNavigable: ParentNavigable) {
31 | _canNavigateToParent = {
32 | return parentNavigable.canNavigateToParent
33 | }
34 | _navigateToParent = {
35 | parentNavigable.navigateToParent()
36 | }
37 | }
38 |
39 | func navigateToParent() {
40 | _navigateToParent()
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/Reconnect/Toolbars/DeviceToolbar.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 |
21 | struct DeviceToolbar: CustomizableToolbarContent {
22 |
23 | @FocusedObject private var deviceProxy: DeviceModelProxy?
24 |
25 | init() {
26 | }
27 |
28 | var body: some CustomizableToolbarContent {
29 |
30 | ToolbarItem(id: "screenshot") {
31 | Button {
32 | deviceProxy?.deviceModel.captureScreenshot()
33 | } label: {
34 | Label("Screenshot", systemImage: "camera.viewfinder")
35 | }
36 | .help("Capture a screenshot of your Psion")
37 | .disabled(deviceProxy?.deviceModel.isCapturingScreenshot ?? true)
38 | }
39 |
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/docs/docs/development/architecture/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Architecture
3 | ---
4 |
5 | # Background
6 |
7 | Psions communicate using the [Psion Link Protocol](https://thoukydides.github.io/riscos-psifs/plp.html) (PLP). Reconnect builds on top of [plptools](https://github.com/plptools/plptools), a tried-and-tested open source implementation of PLP for posix-friendly systems.
8 |
9 | plptools comprises the following components:
10 |
11 | - Service
12 | - `ncpd`---daemon that manages the connection to a single Psion on a serial port and exposes the different services to the local machine over TCP
13 | - Clients
14 | - `plpftp`---CLI offering an FTP-like interface for browsing and transferring files
15 | - `plpfuse`---exposes the connected Psion as local file system using [FUSE](https://en.wikipedia.org/wiki/Filesystem_in_Userspace)
16 | - `plpprint`---print-server allowing you to print-to-files from your Psion
17 | - `sisinstall`---CLI for installing SIS installer files on EPOC32 devices
18 |
19 | ```mermaid
20 | graph TD
21 | plpftp --> ncpd
22 | plpfuse --> ncpd
23 | plpprint --> ncpd
24 | sisinstall --> ncpd
25 | ```
26 |
27 | Unfortunately, the evolving security architecture of macOS is increasingly hostile to services like FUSE (you need to [disable System Integrity Protection](https://developer.apple.com/documentation/security/disabling-and-enabling-system-integrity-protection) to install the required kernel extensions), and having to manually run services like `ncpd` to manage your Psion connection is far from user-friendly, necessitating a different approach on macOS.
28 |
--------------------------------------------------------------------------------
/ReconnectCore/Sources/ReconnectCore/Extensions/NSRunningApplication.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import AppKit
20 |
21 | extension NSRunningApplication {
22 |
23 | public static func terminateRunningApplications(bundleIdentifier: String, waitForCompletion: Bool) {
24 | let runningApps = runningApplications(withBundleIdentifier: bundleIdentifier)
25 | for app in runningApps {
26 | app.terminate()
27 | }
28 | guard waitForCompletion else {
29 | return
30 | }
31 | for app in runningApps {
32 | while !app.isTerminated {
33 | RunLoop.current.run(until: Date().addingTimeInterval(0.1))
34 | }
35 | }
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/scripts/update-release-notes.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Reconnect -- Psion connectivity for macOS
4 | #
5 | # Copyright (C) 2024-2025 Jason Morley
6 | #
7 | # This program is free software; you can redistribute it and/or modify
8 | # it under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation; either version 2 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program; if not, write to the Free Software
19 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 |
21 | set -e
22 | set -o pipefail
23 | set -x
24 | set -u
25 |
26 | SCRIPTS_DIRECTORY="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
27 |
28 | ROOT_DIRECTORY="$SCRIPTS_DIRECTORY/.."
29 | RELEASE_NOTES_TEMPLATE_PATH="$SCRIPTS_DIRECTORY/release-notes.md"
30 | RELEASE_NOTES_DIRECTORY="$ROOT_DIRECTORY/docs/releases"
31 | RELEASE_NOTES_PATH="$RELEASE_NOTES_DIRECTORY/index.md"
32 |
33 | source "$SCRIPTS_DIRECTORY/environment.sh"
34 |
35 | cd "$ROOT_DIRECTORY"
36 |
37 | if [ -d "$RELEASE_NOTES_DIRECTORY" ]; then
38 | rm -r "$RELEASE_NOTES_DIRECTORY"
39 | fi
40 | mkdir -p "$RELEASE_NOTES_DIRECTORY"
41 | changes notes --all --template "$RELEASE_NOTES_TEMPLATE_PATH" > "$RELEASE_NOTES_PATH"
42 |
--------------------------------------------------------------------------------
/Reconnect/Windows/NSInstallerWindow.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 |
21 | class NSInstallerWindow: NSWindow {
22 |
23 | var url: URL?
24 |
25 | convenience init(applicationModel: ApplicationModel, url: URL) {
26 | let windowProxy = WindowProxy()
27 | let rootView = InstallerView(applicationModel: applicationModel, url: url)
28 | .environment(\.window, windowProxy)
29 | self.init(contentViewController: NSHostingController(rootView: rootView))
30 | self.url = url
31 | windowProxy.nsWindow = self
32 | title = url.displayName
33 | styleMask.remove([.resizable, .borderless, .fullSizeContentView])
34 | setContentSize(CGSize(width: 800, height: 600))
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/Reconnect/Software Index/Model/PsionSoftwareIndexError.swift:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2024-2025 Jason Morley
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in all
11 | // copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | // SOFTWARE.
20 |
21 | import Foundation
22 |
23 | enum PsionSoftwareIndexError: Error {
24 | case unknownDownloadFailure
25 | }
26 |
27 | extension PsionSoftwareIndexError: LocalizedError {
28 |
29 | public var errorDescription: String? {
30 | switch self {
31 | case .unknownDownloadFailure:
32 | return "Unknown download failure"
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/Reconnect/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "icon_16x16.png",
5 | "idiom" : "mac",
6 | "scale" : "1x",
7 | "size" : "16x16"
8 | },
9 | {
10 | "filename" : "icon_16x16@2x@2x.png",
11 | "idiom" : "mac",
12 | "scale" : "2x",
13 | "size" : "16x16"
14 | },
15 | {
16 | "filename" : "icon_32x32.png",
17 | "idiom" : "mac",
18 | "scale" : "1x",
19 | "size" : "32x32"
20 | },
21 | {
22 | "filename" : "icon_32x32@2x@2x.png",
23 | "idiom" : "mac",
24 | "scale" : "2x",
25 | "size" : "32x32"
26 | },
27 | {
28 | "filename" : "icon_128x128.png",
29 | "idiom" : "mac",
30 | "scale" : "1x",
31 | "size" : "128x128"
32 | },
33 | {
34 | "filename" : "icon_128x128@2x@2x.png",
35 | "idiom" : "mac",
36 | "scale" : "2x",
37 | "size" : "128x128"
38 | },
39 | {
40 | "filename" : "icon_256x256.png",
41 | "idiom" : "mac",
42 | "scale" : "1x",
43 | "size" : "256x256"
44 | },
45 | {
46 | "filename" : "icon_256x256@2x@2x.png",
47 | "idiom" : "mac",
48 | "scale" : "2x",
49 | "size" : "256x256"
50 | },
51 | {
52 | "filename" : "icon_512x512.png",
53 | "idiom" : "mac",
54 | "scale" : "1x",
55 | "size" : "512x512"
56 | },
57 | {
58 | "filename" : "icon_512x512@2x@2x.png",
59 | "idiom" : "mac",
60 | "scale" : "2x",
61 | "size" : "512x512"
62 | }
63 | ],
64 | "info" : {
65 | "author" : "xcode",
66 | "version" : 1
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/ReconnectMenu/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "icon_16x16.png",
5 | "idiom" : "mac",
6 | "scale" : "1x",
7 | "size" : "16x16"
8 | },
9 | {
10 | "filename" : "icon_16x16@2x@2x.png",
11 | "idiom" : "mac",
12 | "scale" : "2x",
13 | "size" : "16x16"
14 | },
15 | {
16 | "filename" : "icon_32x32.png",
17 | "idiom" : "mac",
18 | "scale" : "1x",
19 | "size" : "32x32"
20 | },
21 | {
22 | "filename" : "icon_32x32@2x@2x.png",
23 | "idiom" : "mac",
24 | "scale" : "2x",
25 | "size" : "32x32"
26 | },
27 | {
28 | "filename" : "icon_128x128.png",
29 | "idiom" : "mac",
30 | "scale" : "1x",
31 | "size" : "128x128"
32 | },
33 | {
34 | "filename" : "icon_128x128@2x@2x.png",
35 | "idiom" : "mac",
36 | "scale" : "2x",
37 | "size" : "128x128"
38 | },
39 | {
40 | "filename" : "icon_256x256.png",
41 | "idiom" : "mac",
42 | "scale" : "1x",
43 | "size" : "256x256"
44 | },
45 | {
46 | "filename" : "icon_256x256@2x@2x.png",
47 | "idiom" : "mac",
48 | "scale" : "2x",
49 | "size" : "256x256"
50 | },
51 | {
52 | "filename" : "icon_512x512.png",
53 | "idiom" : "mac",
54 | "scale" : "1x",
55 | "size" : "512x512"
56 | },
57 | {
58 | "filename" : "icon_512x512@2x@2x.png",
59 | "idiom" : "mac",
60 | "scale" : "2x",
61 | "size" : "512x512"
62 | }
63 | ],
64 | "info" : {
65 | "author" : "xcode",
66 | "version" : 1
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Reconnect/Views/DeviceDriveGroup.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 |
21 | struct DeviceDriveGroup: View {
22 |
23 | @State var isShowingDrives = true
24 |
25 | let deviceModel: DeviceModel
26 |
27 | var body: some View {
28 | DisclosureGroup(isExpanded: $isShowingDrives) {
29 | ForEach(deviceModel.drives) { driveInfo in
30 | Label(driveInfo.displayName, image: driveInfo.image)
31 | .tag(BrowserSection.drive(deviceModel.id, driveInfo))
32 | .id(BrowserSection.drive(deviceModel.id, driveInfo))
33 | }
34 | } label: {
35 | Label("My Psion", image: "Psion16")
36 | }
37 | .tag(BrowserSection.device(deviceModel.id))
38 | .id(BrowserSection.device(deviceModel.id))
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/Reconnect/Views/PixelImage.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 |
21 | import Interact
22 |
23 | struct PixelImage: View {
24 |
25 | enum Source {
26 | case name(String)
27 | case resource(ImageResource)
28 | }
29 |
30 | let source: Source
31 |
32 | init(_ name: String) {
33 | self.source = .name(name)
34 | }
35 |
36 | init(_ resource: ImageResource) {
37 | self.source = .resource(resource)
38 | }
39 |
40 | var image: Image {
41 | switch source {
42 | case .name(let string):
43 | Image(string)
44 | case .resource(let resource):
45 | Image(resource)
46 | }
47 | }
48 |
49 | var body: some View {
50 | image
51 | .interpolation(.none)
52 | .resizable()
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/Reconnect/Views/EditableText.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 |
21 | // This exists as a fairly gnarly workaround to turn SwiftUI's continuous table view text field editing back into
22 | // something that looks vaguely modal. It's possible we'd also get this for free by writing using an NSTextField
23 | // directly, but that's for another day; until then, debouncing edits will have to be sufficient.
24 | struct EditableText: View {
25 |
26 | @StateObject var model: EditableTextModel
27 |
28 | init(initialValue: String, completion: @escaping (String) -> Void) {
29 | _model = StateObject(wrappedValue: EditableTextModel(initialValue: initialValue, completion: completion))
30 | }
31 |
32 | var body: some View {
33 | TextField("", text: $model.text)
34 | .runs(model)
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/Reconnect/Toolbars/RefreshToolbar.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 |
21 | struct RefreshToolbar: CustomizableToolbarContent {
22 |
23 | @FocusedObject private var refreshableProxy: RefreshableProxy?
24 |
25 | var body: some CustomizableToolbarContent {
26 |
27 | if refreshableProxy?.isRefreshing ?? false {
28 | ToolbarItem(id: "refresh-indicator", placement: .navigation) {
29 | ProgressView()
30 | .controlSize(.small)
31 | }
32 | }
33 |
34 | ToolbarItem(id: "refresh") {
35 | Button {
36 | refreshableProxy?.refresh()
37 | } label: {
38 | Label("Refresh", systemImage: "arrow.clockwise")
39 | }
40 | .disabled(!(refreshableProxy?.canRefresh ?? false))
41 | }
42 |
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/ReconnectCore/Sources/ReconnectCore/Utilities/Graphics.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import Foundation
20 | import ImageIO
21 | import UniformTypeIdentifiers
22 |
23 | public func CGImageWrite(destinationURL: URL, images: [CGImage], type: UTType) throws {
24 | guard let destination = CGImageDestinationCreateWithURL(destinationURL as CFURL,
25 | type.identifier as CFString,
26 | images.count,
27 | nil) else {
28 | throw ReconnectError.imageSaveError
29 | }
30 | for image in images {
31 | CGImageDestinationAddImage(destination, image, nil)
32 | }
33 | guard CGImageDestinationFinalize(destination) else {
34 | throw ReconnectError.imageSaveError
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/ReconnectUITests/ReconnectUITestsLaunchTests.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import XCTest
20 |
21 | final class ReconnectUITestsLaunchTests: XCTestCase {
22 |
23 | override class var runsForEachTargetApplicationUIConfiguration: Bool {
24 | true
25 | }
26 |
27 | override func setUpWithError() throws {
28 | continueAfterFailure = false
29 | }
30 |
31 | func testLaunch() throws {
32 | let app = XCUIApplication()
33 | app.launch()
34 |
35 | // Insert steps here to perform after app launch but before taking a screenshot,
36 | // such as logging into a test account or navigating somewhere in the app
37 |
38 | let attachment = XCTAttachment(screenshot: app.screenshot())
39 | attachment.name = "Launch Screen"
40 | attachment.lifetime = .keepAlways
41 | add(attachment)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Reconnect/Model/RefreshableProxy.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 |
21 | class RefreshableProxy: ObservableObject, Refreshable {
22 |
23 | var canRefresh: Bool {
24 | return _canRefresh()
25 | }
26 |
27 | var isRefreshing: Bool {
28 | return _isRefreshing()
29 | }
30 |
31 | private let _canRefresh: @MainActor () -> Bool
32 | private let _isRefreshing: @MainActor () -> Bool
33 | private let _refresh: @MainActor () -> Void
34 |
35 | init(_ refreshable: Refreshable) {
36 | self._canRefresh = {
37 | refreshable.canRefresh
38 | }
39 | self._isRefreshing = {
40 | refreshable.isRefreshing
41 | }
42 | self._refresh = {
43 | refreshable.refresh()
44 | }
45 | }
46 |
47 | func refresh() {
48 | _refresh()
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/Reconnect/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 |
21 | @MainActor
22 | final class AppDelegate: NSObject, NSApplicationDelegate {
23 |
24 | let applicationModel = ApplicationModel()
25 |
26 | func application(_ application: NSApplication, open urls: [URL]) {
27 | for url in urls {
28 | if url.isFileURL {
29 | applicationModel.showInstallerWindow(url: url)
30 | } else if url == .update {
31 | applicationModel.updaterController.updater.checkForUpdates()
32 | } else {
33 | print("Ignoring URL '\(url.absoluteString)'...")
34 | }
35 | }
36 | }
37 |
38 | func applicationWillTerminate(_ notification: Notification) {
39 | if !applicationModel.openAtLogin {
40 | applicationModel.terminateRunningMenuApplications()
41 | }
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/ReconnectCore/Sources/ReconnectCore/XPC/DaemonClientInterface.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import Foundation
20 |
21 | @objc
22 | public protocol DaemonClientInterface {
23 |
24 | func keepalive(count: Int)
25 | func setSerialDevices(_ devices: [SerialDevice])
26 | func setIsConnected(_ isConnected: Bool)
27 |
28 | }
29 |
30 | extension NSXPCInterface {
31 |
32 | static var daemonClientInterface: NSXPCInterface {
33 | let interface = NSXPCInterface(with: DaemonClientInterface.self)
34 | let allowedClasses = [NSArray.self, SerialDevice.self, SerialDeviceConfiguration.self] as NSSet as Set
35 | interface.setClasses(allowedClasses,
36 | for: #selector(DaemonClientInterface.setSerialDevices(_:)),
37 | argumentIndex: 0,
38 | ofReply: false)
39 | return interface
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/Reconnect/Software Index/API/Program.swift:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2024-2025 Jason Morley
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in all
11 | // copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | // SOFTWARE.
20 |
21 | import Foundation
22 |
23 | struct Program: Codable, Identifiable, Hashable {
24 |
25 | let id: String
26 | let name: String
27 | let icon: SoftwareIndexImage?
28 | let versions: [Version]
29 | let subtitle: String?
30 | let description: String?
31 | let tags: [String]
32 | var screenshots: [SoftwareIndexImage]?
33 |
34 | var iconURL: URL? {
35 | guard let icon else {
36 | return nil
37 | }
38 | return URL.softwareIndexAPIV1.appendingPathComponent(icon.path)
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/Reconnect/Software Index/Views/IconView.swift:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2024-2025 Jason Morley
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in all
11 | // copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | // SOFTWARE.
20 |
21 | import SwiftUI
22 |
23 | struct IconView: View {
24 |
25 | let url: URL?
26 |
27 | var body: some View {
28 | if let url {
29 | AsyncImage(url: url) { image in
30 | image
31 | .interpolation(.none)
32 | } placeholder: {
33 | Image(.unknownAppIcon)
34 | .interpolation(.none)
35 | }
36 | } else {
37 | SwiftUI.Image(.unknownAppIcon)
38 | .interpolation(.none)
39 | }
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/Reconnect/Software Index/Styles/UnadornedCircularProgressViewStyle.swift:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2024-2025 Jason Morley
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in all
11 | // copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | // SOFTWARE.
20 |
21 | import SwiftUI
22 |
23 | struct UnadornedCircularProgressViewStyle: ProgressViewStyle {
24 |
25 | func makeBody(configuration: Configuration) -> some View {
26 | ProgressView(value: configuration.fractionCompleted)
27 | .progressViewStyle(.circular)
28 | }
29 |
30 | }
31 |
32 | extension ProgressViewStyle where Self == UnadornedCircularProgressViewStyle {
33 |
34 | static var unadornedCircular: UnadornedCircularProgressViewStyle {
35 | return UnadornedCircularProgressViewStyle()
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/Reconnect/Modifiers/OptionalNavigationSubtitle.swift:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2024-2025 Jason Morley
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in all
11 | // copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | // SOFTWARE.
20 |
21 | import SwiftUI
22 |
23 | struct OptionalNavigationSubtitle: ViewModifier {
24 |
25 | let subtitle: String?
26 |
27 | func body(content: Content) -> some View {
28 | if let subtitle {
29 | content
30 | .navigationSubtitle(subtitle)
31 | } else {
32 | content
33 | }
34 | }
35 |
36 | }
37 |
38 | extension View {
39 |
40 | func optionalNavigationSubtitle(_ subtitle: String?) -> some View {
41 | return self.modifier(OptionalNavigationSubtitle(subtitle: subtitle))
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/Reconnect/Views/DeviceView.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 |
21 | struct DeviceView: View {
22 |
23 | @Environment(DeviceModel.self) private var deviceModel
24 |
25 | var body: some View {
26 | ScrollView {
27 | VStack(alignment: .leading, spacing: 32) {
28 |
29 | MachineDetailsGroup(machineInfo: deviceModel.machineInfo)
30 |
31 | DetailsGroup("Installed Programs") {
32 | ProgramManagerView(deviceModel: deviceModel)
33 | .frame(height: 300)
34 | .border(.quaternary)
35 | }
36 |
37 | }
38 | .padding()
39 | .frame(maxWidth: .infinity, maxHeight: .infinity)
40 | .scenePadding()
41 | }
42 | .background(.textBackgroundColor)
43 | .navigationTitle("My Psion")
44 | .showsDeviceProgress()
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/ReconnectCore/Sources/ReconnectCore/Extensions/UInt32.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 |
21 | public extension UInt32 {
22 |
23 | static let none: Self = 0x00000000
24 |
25 | // UID1
26 | static let directFileStore: Self = 0x10000037
27 | static let permanentFileStoreLayout: Self = 0x10000050 // Database
28 | static let multiBitmapRomImage: Self = 0x10000041
29 | static let dynamicLibraryUid: Self = 0x10000079 // Native app
30 |
31 | // UID2
32 | static let appDllDoc: Self = 0x1000006D
33 | static let mbm: Self = 0x10000042
34 |
35 | // UID3
36 | static let word: Self = 0x1000007F
37 | static let sheet: Self = 0x10000088
38 | static let record: Self = 0x1000007E
39 | static let opl: Self = 0x10000085
40 | static let data: Self = 0x10000086
41 | static let agenda: Self = 0x10000084
42 | static let sketch: Self = 0x1000007D
43 | static let jotter: Self = 0x10000CEA
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/Reconnect/Views/ThumbnailView.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 | import QuickLookThumbnailing
21 |
22 | import Interact
23 |
24 | @MainActor
25 | struct ThumbnailView: View {
26 |
27 | let url: URL
28 | let size: CGSize
29 |
30 | @State var image: NSImage? = nil
31 |
32 | var body: some View {
33 | Image(nsImage: image ?? NSWorkspace.shared.icon(forFile: url.path))
34 | .resizable()
35 | .task {
36 | let thumbnail = try? await QLThumbnailGenerator.shared.thumbnailRepresentation(fileAt: url,
37 | size: size,
38 | scale: 2.0,
39 | iconMode: true)
40 | image = thumbnail?.nsImage
41 | }
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/Reconnect/Views/CheckForUpdatesView.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 |
21 | import Sparkle
22 |
23 | // This is the view for the Check for Updates menu item
24 | // Note this intermediate view is necessary for the disabled state on the menu item to work properly before Monterey.
25 | // See https://stackoverflow.com/questions/68553092/menu-not-updating-swiftui-bug for more info
26 | struct CheckForUpdatesView: View {
27 | @ObservedObject private var checkForUpdatesViewModel: CheckForUpdatesViewModel
28 | private let updater: SPUUpdater
29 |
30 | init(updater: SPUUpdater) {
31 | self.updater = updater
32 |
33 | // Create our view model for our CheckForUpdatesView
34 | self.checkForUpdatesViewModel = CheckForUpdatesViewModel(updater: updater)
35 | }
36 |
37 | var body: some View {
38 | Button("Check for Updates…", action: updater.checkForUpdates)
39 | .disabled(!checkForUpdatesViewModel.canCheckForUpdates)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/ReconnectCore/Sources/ReconnectCore/Extensions/DriveInfo.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import Foundation
20 |
21 | extension FileServer.DriveInfo {
22 |
23 | public var displayName: String {
24 | if let name {
25 | return "\(name) (\(drive):)"
26 | } else {
27 | return "\(drive):"
28 | }
29 | }
30 |
31 | public var image: String {
32 | switch mediaType {
33 | case .notPresent:
34 | return "Drive16"
35 | case .unknown:
36 | return "Drive16"
37 | case .floppy:
38 | return "Drive16"
39 | case .disk:
40 | return "Disk16"
41 | case .compactDisc:
42 | return "Drive16"
43 | case .ram:
44 | return "Drive16"
45 | case .flashDisk:
46 | return "Drive16"
47 | case .rom:
48 | return "Drive16"
49 | case .remote:
50 | return "Drive16"
51 | }
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/ReconnectCore/Sources/ReconnectCore/Extensions/MachineType.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 |
21 | extension RemoteCommandServicesClient.MachineType {
22 |
23 | public var localizedNameKey: LocalizedStringKey {
24 | switch self {
25 | case .unknown:
26 | return "Unknown"
27 | case .pc:
28 | return "PC"
29 | case .mc:
30 | return "MC"
31 | case .hc:
32 | return "HC"
33 | case .series3:
34 | return "Series 3"
35 | case .series3acmx:
36 | return "Series 3a / Series 3c / Series 3mx"
37 | case .workabout:
38 | return "Workabout"
39 | case .siena:
40 | return "Siena"
41 | case .series3c:
42 | return "Series 3c"
43 | case .series5:
44 | return "Series 5 / Series 5mx / Series 7 / netBook"
45 | case .winC:
46 | return "WinC"
47 | }
48 |
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/Reconnect/Commands/DeviceCommands.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 |
21 | public struct DeviceCommands: Commands {
22 |
23 | @Environment(ApplicationModel.self) private var applicationModel: ApplicationModel
24 |
25 | @FocusedObject private var deviceProxy: DeviceModelProxy?
26 |
27 | public var body: some Commands {
28 |
29 | CommandMenu("Device") {
30 |
31 | Button {
32 | deviceProxy?.deviceModel.captureScreenshot()
33 | } label: {
34 | Label("Capture Screenshot", systemImage: "camera.viewfinder")
35 | }
36 | .keyboardShortcut("S", modifiers: [.command, .shift])
37 | .disabled(deviceProxy?.deviceModel.isCapturingScreenshot ?? true)
38 |
39 | Divider()
40 |
41 | Button("Install Reconnect Tools...") {
42 | applicationModel.installGuestTools()
43 | }
44 | .disabled(deviceProxy == nil)
45 |
46 | }
47 |
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/docs/docs/getting-started/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Getting Started
3 | layout: documentation
4 | priority: 100
5 | ---
6 |
7 | # Hardware
8 |
9 | In order to connect your Psion to your Mac, you'll need a couple of bits of hardware: an RS232 adapter, and a suitable link cable.
10 |
11 | ## RS232 Adapter
12 |
13 | RS232 adapters using an [FTDI](https://ftdichip.com) chipset are the most reliable on macOS and are recommended. [This one](https://www.amazon.com/dp/B09WJS26WX) from Amazon US seems to work well.
14 |
15 | > [!IMPORTANT]
16 | > The RS232 adapter you select _must_ support the DSR/DTR hardware control signals to work with Reconnect and plptools. [Prolific](https://www.prolific.com.tw/) adapters can be made to work, but the default drivers that ship by default with macOS don't handle these correctly so you'll need to install the [official drivers](https://apps.apple.com/gb/app/pl2303-serial/id1624835354).
17 |
18 | [Alex Brown](https://oldbytes.space/@thelastpsion) has done some pretty rigorous testing of different adapters which you can find on his [Hackaday blog](https://hackaday.io/project/161291-the-last-psion/log/222358-usb-rs232-shenanigans-updated-2025-05-28).
19 |
20 | ## Link Cable
21 |
22 | The link cable you need depends on the type of Psion you have. Unfortunately these can be a little expensive now, but [Psionex](https://psionex.co.uk) still have stock at relatively competitive prices.
23 |
24 | ### Series 3 and 3a
25 |
26 | - [3Link for Psion Series 3, and 3a](https://psionex.co.uk/en/product/pda/series3/adapters-cables-modems/c-3l-complete.html)
27 |
28 | ### Series 3c, 3mx, 5, 5mx, 7, and netBook
29 |
30 | - [Link cable Psion Series 3c, 3mx, 5, 5mx, 7, and netBook](https://psionex.co.uk/en/product/pda/netbook/adapters-cables-modems/s5mx-rs232-link.html)
31 |
32 | > [!NOTE]
33 | > These are commonly known as 'Honda' cables as Honda manufactured the original connectors.
34 |
--------------------------------------------------------------------------------
/Reconnect/Views/FileTypePopover.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 |
21 | import ReconnectCore
22 |
23 | struct FileTypePopover: View {
24 |
25 | @State var isPresented: Bool = false
26 |
27 | let file: FileServer.DirectoryEntry
28 |
29 | var body: some View {
30 | Button {
31 | isPresented = true
32 | } label: {
33 | Text(file.fileType.name)
34 | }
35 | .popover(isPresented: $isPresented) {
36 | Grid {
37 | GridRow {
38 | Text("UID1")
39 | Text(String(format: "0x%08X", file.uid1))
40 | }
41 | GridRow {
42 | Text("UID2")
43 | Text(String(format: "0x%08X", file.uid2))
44 | }
45 | GridRow {
46 | Text("UID3")
47 | Text(String(format: "0x%08X", file.uid3))
48 | }
49 | }
50 | .padding()
51 | .textSelection(.enabled)
52 | }
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/ReconnectCore/Sources/ReconnectCore/XPC/DaemonInterface.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import Foundation
20 |
21 | @objc
22 | public protocol DaemonInterface {
23 |
24 | func connect(reply: @escaping (DaemonInfo) -> Void)
25 | func configureSerialDevice(path: String, configuration: SerialDeviceConfiguration)
26 |
27 | }
28 |
29 | extension NSXPCInterface {
30 |
31 | static var daemonInterface: NSXPCInterface {
32 | let interface = NSXPCInterface(with: DaemonInterface.self)
33 |
34 | interface.setClasses([DaemonInfo.self] as NSSet as Set,
35 | for: #selector(DaemonInterface.connect(reply:)),
36 | argumentIndex: 0,
37 | ofReply: true)
38 | interface.setClasses([SerialDeviceConfiguration.self] as NSSet as Set,
39 | for: #selector(DaemonInterface.configureSerialDevice(path:configuration:)),
40 | argumentIndex: 1,
41 | ofReply: false)
42 |
43 | return interface
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/Reconnect/Views/Settings/SettingsButton.swift:
--------------------------------------------------------------------------------
1 | // Reconnect -- Psion connectivity for macOS
2 | //
3 | // Copyright (C) 2024-2025 Jason Morley
4 | //
5 | // This program is free software; you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation; either version 2 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program; if not, write to the Free Software
17 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 |
19 | import SwiftUI
20 |
21 | import Diligence
22 | import Interact
23 |
24 | struct SettingsButton