Revision: 13793
http://sourceforge.net/p/skim-app/code/13793
Author: hofman
Date: 2023-11-24 10:14:04 +0000 (Fri, 24 Nov 2023)
Log Message:
-----------
Add option to get an XPC based skimnotes agent
Modified Paths:
--------------
trunk/SkimNotes/SKNAgentListener.h
trunk/SkimNotes/SKNAgentListener.m
trunk/SkimNotes/SkimNotes.xcodeproj/project.pbxproj
trunk/SkimNotes/skimnotes.m
Added Paths:
-----------
trunk/SkimNotes/SKNXPCAgentListenerProtocol.h
Modified: trunk/SkimNotes/SKNAgentListener.h
===================================================================
--- trunk/SkimNotes/SKNAgentListener.h 2023-11-21 18:21:51 UTC (rev 13792)
+++ trunk/SkimNotes/SKNAgentListener.h 2023-11-24 10:14:04 UTC (rev 13793)
@@ -38,15 +38,18 @@
#import <Foundation/Foundation.h>
+@interface SKNAgentListener : NSObject
+{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-@interface SKNAgentListener : NSObject <NSConnectionDelegate>
-{
NSConnection *connection;
+#pragma clang diagnostic pop
+#if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >=
MAC_OS_X_VERSION_10_8
+ NSXPCListener *xpcListener;
+ NSXPCConnection *xpcConnection;
+#endif
}
-#pragma clang diagnostic pop
-- (id)initWithServerName:(NSString *)serverName;
-- (void)destroyConnection;
+- (id)initWithServerName:(NSString *)serverName xpc:(BOOL)isXPC;
@end
Modified: trunk/SkimNotes/SKNAgentListener.m
===================================================================
--- trunk/SkimNotes/SKNAgentListener.m 2023-11-21 18:21:51 UTC (rev 13792)
+++ trunk/SkimNotes/SKNAgentListener.m 2023-11-24 10:14:04 UTC (rev 13793)
@@ -39,33 +39,62 @@
#import "SKNAgentListenerProtocol.h"
#import "NSFileManager_SKNToolExtensions.h"
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+@interface SKNAgentListener (SKNConnection) <NSConnectionDelegate>
+#pragma clang diagnostic pop
+- (BOOL)startConnectionWithServerName:(NSString *)serverName;
+- (void)destroyConnection;
+@end
+
+#pragma mark -
+
+#if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >=
MAC_OS_X_VERSION_10_8
+@interface SKNAgentListener (SKNXPCConnection) <NSXPCListenerDelegate>
+- (BOOL)startXPCListenerWithServerName:(NSString *)serverName;
+- (void)destroyXPCConnection;
+@end
+#else
+#define NSAppKitVersionNumber10_8 1187
+#endif
+
+#pragma mark -
+
@implementation SKNAgentListener
-- (id)initWithServerName:(NSString *)serverName;
+- (id)initWithServerName:(NSString *)serverName xpc:(BOOL)isXPC;
{
self = [super init];
if (self) {
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- connection = [[NSConnection alloc] initWithReceivePort:[NSPort port]
sendPort:nil];
-#pragma clang diagnostic pop
- NSProtocolChecker *checker = [NSProtocolChecker
protocolCheckerWithTarget:self protocol:@protocol(SKNAgentListenerProtocol)];
- [connection setRootObject:checker];
- [connection setDelegate:self];
+ // user can pass nil, in which case we generate a server name to be
read from standard output
+ if (nil == serverName) {
+ if (isXPC)
+ serverName = [NSString
stringWithFormat:@"net.sourceforge.skim-app.skimnotes-%@", [[NSProcessInfo
processInfo] globallyUniqueString]];
+ else
+ serverName = [[NSProcessInfo processInfo]
globallyUniqueString];
+ }
- // user can pass nil, in which case we generate a server name to be
read from standard output
- if (nil == serverName)
- serverName = [[NSProcessInfo processInfo] globallyUniqueString];
-
- if ([connection registerName:serverName] == NO) {
- fprintf(stderr, "skimnotes agent pid %d: unable to register
connection name %s; another process must be running\n", getpid(), [serverName
UTF8String]);
- [self destroyConnection];
+ BOOL success = NO;
+ if (isXPC) {
+#if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >=
MAC_OS_X_VERSION_10_8
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
+ if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_8)
+#endif
+ success = [self startXPCListenerWithServerName:serverName];
+#endif
+ } else {
+ success = [self startConnectionWithServerName:serverName];
+ }
+
+ if (success) {
+ NSFileHandle *fh = [NSFileHandle fileHandleWithStandardOutput];
+ [fh writeData:[serverName dataUsingEncoding:NSUTF8StringEncoding]];
+ [fh closeFile];
+ } else {
[self release];
self = nil;
}
- NSFileHandle *fh = [NSFileHandle fileHandleWithStandardOutput];
- [fh writeData:[serverName dataUsingEncoding:NSUTF8StringEncoding]];
- [fh closeFile];
}
return self;
}
@@ -74,9 +103,37 @@
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self destroyConnection];
+#if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >=
MAC_OS_X_VERSION_10_8
+ [self destroyXPCConnection];
+#endif
[super dealloc];
}
+@end
+
+#pragma mark -
+
+@implementation SKNAgentListener (SKNConnection)
+
+- (BOOL)startConnectionWithServerName:(NSString *)serverName
+{
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ connection = [[NSConnection alloc] initWithReceivePort:[NSPort port]
sendPort:nil];
+#pragma clang diagnostic pop
+ NSProtocolChecker *checker = [NSProtocolChecker
protocolCheckerWithTarget:self protocol:@protocol(SKNAgentListenerProtocol)];
+ [connection setRootObject:checker];
+ [connection setDelegate:self];
+
+ if ([connection registerName:serverName] == NO) {
+ fprintf(stderr, "skimnotes agent pid %d: unable to register connection
name %s; another process must be running\n", getpid(), [serverName UTF8String]);
+ [self destroyConnection];
+ return NO;
+ }
+
+ return YES;
+}
+
- (void)destroyConnection;
{
[connection registerName:nil];
@@ -136,3 +193,82 @@
}
@end
+
+#pragma mark -
+
+#if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >=
MAC_OS_X_VERSION_10_8
+@implementation SKNAgentListener (SKNXPCConnection)
+
+- (BOOL)startXPCListenerWithServerName:(NSString *)serverName
+{
+ xpcListener = [[NSXPCListener alloc] initWithMachServiceName:serverName];
+ [xpcListener setDelegate:self];
+ [xpcListener resume];
+
+ return YES;
+}
+
+- (void)destroyXPCConnection
+{
+ [xpcConnection invalidate];
+ [xpcConnection release];
+ xpcConnection = nil;
+
+ [xpcListener invalidate];
+ [xpcListener release];
+ xpcListener = nil;
+}
+
+// first app to connect will be the owner of this instance of the program;
when the connection dies, so do we
+- (BOOL)listener:(NSXPCListener *)listener
shouldAcceptNewConnection:(NSXPCConnection *)newConnection
+{
+ if (xpcConnection) {
+ [newConnection invalidate];
+ return NO;
+ }
+
+ [newConnection setExportedInterface:[NSXPCInterface
interfaceWithProtocol:@protocol(SKNXPCAgentListenerProtocol)]];
+ NSString *description = [newConnection description];
+ [newConnection setInvalidationHandler:^{
+ [self destroyXPCConnection];
+ fprintf(stderr, "skimnotes agent pid %d dying because port %s is
invalid\n", getpid(), [description UTF8String]);
+ exit(0);
+ }];
+ xpcConnection = [newConnection retain];
+ [xpcConnection resume];
+
+ return YES;
+}
+
+#pragma mark SKNXPCAgentListenerProtocol
+
+- (void)SkimNotesAtPath:(NSString *)aFile reply:(void (^)(NSData *))reply
+{
+ NSError *error = nil;
+ NSData *data = [[NSFileManager defaultManager] SkimNotesAtPath:aFile
error:&error];
+ if (nil == data)
+ fprintf(stderr, "skimnotes agent pid %d: error getting Skim notes
(%s)\n", getpid(), [[error description] UTF8String]);
+ reply(data);
+}
+
+- (void)RTFNotesAtPath:(NSString *)aFile reply:(void (^)(NSData *))reply
+{
+ NSError *error = nil;
+ NSData *data = [[NSFileManager defaultManager] SkimRTFNotesAtPath:aFile
error:&error];
+ if (nil == data)
+ fprintf(stderr, "skimnotes agent pid %d: error getting RTF notes
(%s)\n", getpid(), [[error description] UTF8String]);
+ reply(data);
+}
+
+- (void)textNotesAtPath:(NSString *)aFile encoding:(NSStringEncoding)encoding
reply:(void (^)(NSData *))reply
+{
+ NSError *error = nil;
+ NSString *string = [[NSFileManager defaultManager]
SkimTextNotesAtPath:aFile error:&error];
+ if (nil == string)
+ fprintf(stderr, "skimnotes agent pid %d: error getting text notes
(%s)\n", getpid(), [[error description] UTF8String]);
+ // Returning the string directly can fail under some conditions. For some
strings with corrupt copy-paste characters (typical for notes), -[NSString
canBeConvertedToEncoding:NSUTF8StringEncoding] returns YES but the actual
conversion fails. A result seems to be that encoding the string also fails,
which causes the DO client to get a timeout. Returning NSUnicodeStringEncoding
data seems to work in those cases (and is safe since we're not going over the
wire between big/little-endian systems).
+ reply([string dataUsingEncoding:encoding]);
+}
+
+@end
+#endif
Added: trunk/SkimNotes/SKNXPCAgentListenerProtocol.h
===================================================================
--- trunk/SkimNotes/SKNXPCAgentListenerProtocol.h
(rev 0)
+++ trunk/SkimNotes/SKNXPCAgentListenerProtocol.h 2023-11-24 10:14:04 UTC
(rev 13793)
@@ -0,0 +1,45 @@
+//
+// SKNXPCAgentListenerProtocol.h
+// SkimNotes
+//
+// Created by Christiaan Hofman on 24/11/2023.
+/*
+This software is Copyright (c) 2023
+Christiaan Hofman. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in
+the documentation and/or other materials provided with the
+distribution.
+
+- Neither the name of Adam Maxwell nor the names of any
+contributors may be used to endorse or promote products derived
+from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+@protocol SKNXPCAgentListenerProtocol
+
+- (void)SkimNotesAtPath:(NSString *)aFile reply:(void (^)(NSData *))reply;
+- (void)RTFNotesAtPath:(NSString *)aFile reply:(void (^)(NSData *))reply;
+- (void)textNotesAtPath:(NSString *)aFile encoding:(NSStringEncoding)encoding
reply:(void (^)(NSData *))reply;
+
+@end
Modified: trunk/SkimNotes/SkimNotes.xcodeproj/project.pbxproj
===================================================================
--- trunk/SkimNotes/SkimNotes.xcodeproj/project.pbxproj 2023-11-21 18:21:51 UTC
(rev 13792)
+++ trunk/SkimNotes/SkimNotes.xcodeproj/project.pbxproj 2023-11-24 10:14:04 UTC
(rev 13793)
@@ -268,6 +268,7 @@
CEDB9AFE0E2E9F8A0057FD09 /* en */ = {isa = PBXFileReference;
lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib;
sourceTree = "<group>"; };
CEDB9B0D0E2EA1380057FD09 /* SkimNotes-App.xcconfig */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path =
"SkimNotes-App.xcconfig"; sourceTree = "<group>"; };
CEDB9B200E2EA2F30057FD09 /* main.m */ = {isa =
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path
= main.m; sourceTree = "<group>"; };
+ CEED30532B10AE38003F138F /* SKNXPCAgentListenerProtocol.h */ =
{isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path =
SKNXPCAgentListenerProtocol.h; sourceTree = "<group>"; };
CEF57FCF2988170B00594EC0 /* PDFKit.framework */ = {isa =
PBXFileReference; lastKnownFileType = wrapper.framework; name =
PDFKit.framework; path = System/Library/Frameworks/PDFKit.framework; sourceTree
= SDKROOT; };
CEF57FD12988171C00594EC0 /* Foundation.framework */ = {isa =
PBXFileReference; lastKnownFileType = wrapper.framework; name =
Foundation.framework; path = System/Library/Frameworks/Foundation.framework;
sourceTree = SDKROOT; };
CEF57FD32988172600594EC0 /* AppKit.framework */ = {isa =
PBXFileReference; lastKnownFileType = wrapper.framework; name =
AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree
= SDKROOT; };
@@ -489,6 +490,7 @@
CEBA2B5A0E0566DF0000B2E6 /* SKNAgentListener.h
*/,
CEBA2B5B0E0566DF0000B2E6 /* SKNAgentListener.m
*/,
CEBA2B5C0E0566DF0000B2E6 /*
SKNAgentListenerProtocol.h */,
+ CEED30532B10AE38003F138F /*
SKNXPCAgentListenerProtocol.h */,
);
name = "SkimNotes Tool Classes";
sourceTree = "<group>";
Modified: trunk/SkimNotes/skimnotes.m
===================================================================
--- trunk/SkimNotes/skimnotes.m 2023-11-21 18:21:51 UTC (rev 13792)
+++ trunk/SkimNotes/skimnotes.m 2023-11-24 10:14:04 UTC (rev 13793)
@@ -49,8 +49,8 @@
" skimnotes convert [-s|-n] IN_PDF_FILE
[OUT_PDF_FILE]\n"
" skimnotes format archive|plist|text|rtf
IN_SKIM_FILE|- [OUT_FILE|-]\n"
" skimnotes offset DX DY IN_SKIM_FILE|-
[OUT_SKIM_FILE|-]\n"
- " skimnotes agent [SERVER_NAME]\n"
- " skimnotes protocol\n"
+ " skimnotes agent [-xpc] [SERVER_NAME]\n"
+ " skimnotes protocol [-xpc]\n"
" skimnotes help [VERB]\n"
" skimnotes version";
static char *versionStr = "SkimNotes command-line client, version 2.9.3";
@@ -88,18 +88,15 @@
"Offsets all notes in IN_SKIM_FILE or standard
input by an amount (DX, DY) and writes the result to OUT_SKIM_FILE or standard
output.\n"
"Writes back to IN_SKIM_FILE (or standard output)
if OUT_SKIM_FILE is not provided.";
static char *agentHelpStr = "skimnotes agent: run the Skim Notes agent\n"
- "Usage: skimnotes agent [SERVER_NAME]\n\n"
+ "Usage: skimnotes agent [-xpc] [SERVER_NAME]\n\n"
"Runs a Skim Notes agent server with server name
SERVER_NAME, to which a Cocoa application can connect using DO.\n"
+ "Runs a XPC based agent when the -xpc option is
provided.\n"
"When SERVER_NAME is not provided, a unique name
is generated and returned on standard output.\n"
- "The DO server conforms to the following formal
protocol.\n\n"
- "@protocol SKNAgentListenerProtocol\n"
- "- (bycopy NSData *)SkimNotesAtPath:(in bycopy
NSString *)aFile;\n"
- "- (bycopy NSData *)RTFNotesAtPath:(in bycopy
NSString *)aFile;\n"
- "- (bycopy NSData *)textNotesAtPath:(in bycopy
NSString *)aFile encoding:(NSStringEncoding)encoding;\n"
- "@end";
+ "The DO server conforms to the formal protocol
returned by the protocol action.";
static char *protocolHelpStr = "skimnotes protocol: write the DO server
protocol to standard output\n"
- "Usage: skimnotes protocol\n\n"
- "Write the DO server protocol for the agent to
standard output.";
+ "Usage: skimnotes protocol [-xpc]\n\n"
+ "Write the DO server protocol for the agent to
standard output."
+ "Returns a protocol for a XPC connection when
the -xpc option is provided.\n";
static char *helpHelpStr = "skimnotes help: get help on the skimnotes tool\n"
"Usage: skimnotes help [VERB]\n\n"
"Get help on the verb VERB.";
@@ -113,6 +110,12 @@
"- (bycopy NSData *)textNotesAtPath:(in bycopy
NSString *)aFile encoding:(NSStringEncoding)encoding;\n"
"@end";
+static char *xpcProtocolStr = "@protocol SKNAgentListenerProtocol\n"
+ "- (void)SkimNotesAtPath:(NSString *)aFile
reply:(void (^)(NSData *))reply;\n"
+ "- (void)RTFNotesAtPath:(NSString *)aFile
reply:(void (^)(NSData *))reply;\n"
+ "- (void)textNotesAtPath:(NSString *)aFile
encoding:(NSStringEncoding)encoding reply:(void (^)(NSData *))reply;\n"
+ "@end";
+
#define ACTION_GET_STRING @"get"
#define ACTION_SET_STRING @"set"
#define ACTION_REMOVE_STRING @"remove"
@@ -125,6 +128,8 @@
#define ACTION_VERSION_STRING @"version"
#define ACTION_HELP_STRING @"help"
+#define XPC_OPTION_STRING @"-xpc"
+
#define FORMAT_OPTION_STRING @"-format"
#define SYNCABLE_OPTION_STRING @"-s"
#define NONSYNCABLE_OPTION_STRING @"-n"
@@ -248,9 +253,11 @@
} else if (action == SKNActionAgent) {
- NSString *serverName = [args count] > 2 ? [args lastObject] : nil;
- SKNAgentListener *listener = [[SKNAgentListener alloc]
initWithServerName:serverName];
+ BOOL isXPC = ([args count] > 2 && [[args objectAtIndex:2]
isEqualToString:XPC_OPTION_STRING]);
+ NSString *serverName = [args count] > (isXPC ? 3 : 2) ? [args
lastObject] : nil;
+ SKNAgentListener *listener = [[SKNAgentListener alloc]
initWithServerName:serverName xpc:isXPC];
+
NSRunLoop *rl = [NSRunLoop currentRunLoop];
BOOL didRun;
@@ -266,7 +273,11 @@
} else if (action == SKNActionProtocol) {
- WRITE_OUT(protocolStr);
+ if ([args count] > 2 && [[args objectAtIndex:2]
isEqualToString:XPC_OPTION_STRING]) {
+ WRITE_OUT(xpcProtocolStr);
+ } else {
+ WRITE_OUT(protocolStr);
+ }
} else if (action == SKNActionHelp) {
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
_______________________________________________
Skim-app-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/skim-app-commit