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

Reply via email to