1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-11-23 21:17:42 +00:00

Compare commits

...

5 Commits

Author SHA1 Message Date
Thomas Harte
74736d9723 Reconfigure towards permission on demand. 2025-11-23 12:15:08 -05:00
Thomas Harte
5c20fcefc4 Add delegate to validate on a file-by-file basis. 2025-11-23 12:11:47 -05:00
Thomas Harte
e9ba1c4ede Add some text. 2025-11-23 00:33:08 -05:00
Thomas Harte
6767ddde4c Further expand TODOs and notes. 2025-11-23 00:27:23 -05:00
Thomas Harte
56a326d7db Add some attempt to obtain user permissions for folder access. 2025-11-23 00:07:32 -05:00
5 changed files with 158 additions and 29 deletions

View File

@@ -68,6 +68,7 @@ class MachineDocument:
var fileObserver: CSFileContentChangeObserver? var fileObserver: CSFileContentChangeObserver?
override func read(from url: URL, ofType typeName: String) throws { override func read(from url: URL, ofType typeName: String) throws {
if let analyser = CSStaticAnalyser(fileAt: url) { if let analyser = CSStaticAnalyser(fileAt: url) {
checkPermisions(analyser.mediaSet)
self.displayName = analyser.displayName self.displayName = analyser.displayName
self.configureAs(analyser) self.configureAs(analyser)
self.fileObserver = CSFileContentChangeObserver.init(url: url, handler: { self.fileObserver = CSFileContentChangeObserver.init(url: url, handler: {
@@ -332,6 +333,7 @@ class MachineDocument:
private func insertFile(_ URL: URL) { private func insertFile(_ URL: URL) {
// Try to insert media. // Try to insert media.
let mediaSet = CSMediaSet(fileAt: URL) let mediaSet = CSMediaSet(fileAt: URL)
checkPermisions(mediaSet)
if !mediaSet.empty { if !mediaSet.empty {
mediaSet.apply(to: self.machine) mediaSet.apply(to: self.machine)
return return
@@ -347,6 +349,10 @@ class MachineDocument:
} }
} }
private func checkPermisions(_ mediaSet: CSMediaSet) {
mediaSet.addPermissionHandler()
}
// MARK: - Input Management. // MARK: - Input Management.
/// Upon a resign key, immediately releases all ongoing input mechanisms any currently pressed keys, /// Upon a resign key, immediately releases all ongoing input mechanisms any currently pressed keys,

View File

@@ -12,6 +12,16 @@ NS_ASSUME_NONNULL_BEGIN
@class CSMachine; @class CSMachine;
@interface CSMediaSet : NSObject
- (instancetype)initWithFileAtURL:(NSURL *)url;
- (void)applyToMachine:(CSMachine *)machine;
- (void)addPermissionHandler;
@property(nonatomic, readonly) BOOL empty;
@end
typedef NS_ENUM(NSInteger, CSMachineAmigaModel) { typedef NS_ENUM(NSInteger, CSMachineAmigaModel) {
CSMachineAmigaModelA500, CSMachineAmigaModelA500,
}; };
@@ -171,15 +181,7 @@ typedef int Kilobytes;
@property(nonatomic, readonly, nullable) NSString *optionsNibName; @property(nonatomic, readonly, nullable) NSString *optionsNibName;
@property(nonatomic, readonly) NSString *displayName; @property(nonatomic, readonly) NSString *displayName;
@property(nonatomic, readonly, nonnull) CSMediaSet *mediaSet;
@end
@interface CSMediaSet : NSObject
- (instancetype)initWithFileAtURL:(NSURL *)url;
- (void)applyToMachine:(CSMachine *)machine;
@property(nonatomic, readonly) BOOL empty;
@end @end

View File

@@ -30,6 +30,113 @@
#import "Clock_Signal-Swift.h" #import "Clock_Signal-Swift.h"
namespace {
struct PermissionDelegate: public Storage::FileBundle::FileBundle::PermissionDelegate {
void validate_open(const std::string &, Storage::FileMode) {
// TODO.
// // (1) Does this file bundle have a base path?
// const auto path = bundle->base_path();
// if(!path.has_value()) {
// continue;
// }
// NSString *pathName = [NSString stringWithUTF8String:path->c_str()];
//
// // (2) Can everything in that base path already be freely read?
// NSError *error;
// NSArray<NSString *> *allFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:pathName error:&error];
// BOOL hasFullAccess = YES;
// for(NSString *file in allFiles) {
// FILE *pilot = fopen([[pathName stringByAppendingPathComponent:file] UTF8String], "rb");
// if(!pilot) {
// hasFullAccess = NO;
// break;
// }
// fclose(pilot);
// }
// if(hasFullAccess) {
// continue;
// }
//
// // (3) Ask the user for permission.
// NSOpenPanel *request = [NSOpenPanel openPanel];
// request.prompt = @"Grant Permission";
// request.message = @"Please Grant Permission For Full Folder Access";
// request.canChooseFiles = NO;
// request.allowsMultipleSelection = NO;
// request.canChooseDirectories = YES;
// [request setDirectoryURL:[NSURL fileURLWithPath:pathName isDirectory:YES]];
//
// request.accessoryView = [NSTextField labelWithString:
// @"Clock Signal is sandboxed; it cannot access any of your files without explicit permission.\n"
// @"The type of program you are loading might require access to other files in its directory, which this "
// @"application does not currently have permission to do.\n"
// @"Please select 'Grant Permission' to give it permission to do so."
// ];
// request.accessoryViewDisclosed = YES;
// // TODO: use delegate further to shepherd user.
//
// const auto response = [request runModal];
// if(response != NSModalResponseOK) {
// continue;
// }
//
// // Possibly substitute the base path, in case the one returned
// // is an indirection out of the sandbox.
// if(![request.URL isEqual:[NSURL fileURLWithPath:pathName]]) {
// NSLog(@"Need to substitute: %@", request.URL);
// }
//
// // TODO: bookmarkDataWithOptions on the URL, and store that somewhere for
// // later retrieval. Then try that again if the same directory presents itself.
}
void validate_erase(const std::string &) {
// Currently a no-op, as it so happens that the only machine that currently
// uses a file bundle is the Enterprise, and its semantics involve opening
// a file before it can be erased.
}
};
PermissionDelegate permission_delegate;
}
@implementation CSMediaSet {
Analyser::Static::Media _media;
}
- (instancetype)initWithMedia:(Analyser::Static::Media)media {
self = [super init];
if(self) {
_media = media;
}
return self;
}
- (instancetype)initWithFileAtURL:(NSURL *)url {
self = [super init];
if(self) {
_media = Analyser::Static::GetMedia([url fileSystemRepresentation]);
}
return self;
}
- (BOOL)empty {
return _media.empty();
}
- (void)applyToMachine:(CSMachine *)machine {
[machine applyMedia:_media];
}
- (void)addPermissionHandler {
for(const auto &bundle: _media.file_bundles) {
bundle->set_permission_delegate(&permission_delegate);
}
}
@end
@implementation CSStaticAnalyser { @implementation CSStaticAnalyser {
Analyser::Static::TargetList _targets; Analyser::Static::TargetList _targets;
} }
@@ -438,26 +545,12 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K
return _targets; return _targets;
} }
@end - (nonnull CSMediaSet *)mediaSet {
Analyser::Static::Media net;
@implementation CSMediaSet { for(const auto &target: _targets) {
Analyser::Static::Media _media; net += target->media;
}
- (instancetype)initWithFileAtURL:(NSURL *)url {
self = [super init];
if(self) {
_media = Analyser::Static::GetMedia([url fileSystemRepresentation]);
} }
return self; return [[CSMediaSet alloc] initWithMedia:net];
}
- (void)applyToMachine:(CSMachine *)machine {
[machine applyMedia:_media];
}
- (BOOL)empty {
return _media.empty();
} }
@end @end

View File

@@ -26,10 +26,26 @@ std::optional<std::string> LocalFSFileBundle::key_file() {
return key_file_; return key_file_;
} }
void LocalFSFileBundle::set_permission_delegate(PermissionDelegate *const delegate) {
permission_delegate_ = delegate;
}
Storage::FileHolder LocalFSFileBundle::open(const std::string &name, const Storage::FileMode mode) { Storage::FileHolder LocalFSFileBundle::open(const std::string &name, const Storage::FileMode mode) {
return Storage::FileHolder(base_path_ + name, mode); const auto full_name = base_path_ + name;
if(permission_delegate_) {
permission_delegate_->validate_open(full_name, mode);
}
return Storage::FileHolder(full_name, mode);
} }
bool LocalFSFileBundle::erase(const std::string &name) { bool LocalFSFileBundle::erase(const std::string &name) {
const auto full_name = base_path_ + name;
if(permission_delegate_) {
permission_delegate_->validate_erase(full_name);
}
return !remove((base_path_ + name).c_str()); return !remove((base_path_ + name).c_str());
} }
std::optional<std::string> LocalFSFileBundle::base_path() {
return base_path_;
}

View File

@@ -23,9 +23,17 @@ namespace Storage::FileBundle {
bundles in the future. bundles in the future.
*/ */
struct FileBundle { struct FileBundle {
struct PermissionDelegate {
virtual void validate_open(const std::string &, FileMode) = 0;
virtual void validate_erase(const std::string &) = 0;
};
virtual std::optional<std::string> key_file() = 0; virtual std::optional<std::string> key_file() = 0;
virtual FileHolder open(const std::string &, FileMode) = 0; virtual FileHolder open(const std::string &, FileMode) = 0;
virtual bool erase(const std::string &) = 0; virtual bool erase(const std::string &) = 0;
virtual std::optional<std::string> base_path() = 0;
virtual void set_permission_delegate(PermissionDelegate *) = 0;
}; };
@@ -36,9 +44,13 @@ struct LocalFSFileBundle: public FileBundle {
FileHolder open(const std::string &, FileMode) override; FileHolder open(const std::string &, FileMode) override;
bool erase(const std::string &) override; bool erase(const std::string &) override;
std::optional<std::string> base_path() override;
void set_permission_delegate(PermissionDelegate *) override;
private: private:
std::string key_file_; std::string key_file_;
std::string base_path_; std::string base_path_;
PermissionDelegate *permission_delegate_ = nullptr;
}; };
}; };