99@interface RVLinkURLHandler : NSObject {
1010 BOOL urlProcessed;
1111 NSAlert *currentAlert;
12+ NSString *latestRVLinkURL;
1213}
1314- (void )handleGetURLEvent : (NSAppleEventDescriptor *)event withReplyEvent : (NSAppleEventDescriptor *)replyEvent ;
1415- (void )processRVLinkURL : (NSString *)rvlinkURL ;
1516- (BOOL )hasProcessedURL ;
1617- (NSMutableArray <NSURL *> *)findRVAppsUsingMDFind ;
1718- (NSMutableArray <NSURL *> *)findRVAppsUsingWorkspace : (NSString *)rvlinkURL ;
18- + (BOOL )terminateOtherInstancesIfNeeded ;
1919@end
2020
2121@implementation RVLinkURLHandler
@@ -24,6 +24,7 @@ - (instancetype)init {
2424 if (self) {
2525 urlProcessed = NO ;
2626 currentAlert = nil ;
27+ latestRVLinkURL = nil ;
2728 }
2829 return self;
2930}
@@ -32,51 +33,42 @@ - (BOOL)hasProcessedURL {
3233 return urlProcessed;
3334}
3435
35- + (BOOL )terminateOtherInstancesIfNeeded {
36- NSString *bundleID = [[NSBundle mainBundle ] bundleIdentifier ];
37- NSArray <NSRunningApplication *> *runningInstances =
38- [NSRunningApplication runningApplicationsWithBundleIdentifier: bundleID];
39-
40- int currentPID = [[NSProcessInfo processInfo ] processIdentifier ];
41-
42- // Terminate any other running instances to prevent multiple dialogs
43- for (NSRunningApplication *app in runningInstances) {
44- int instancePID = [app processIdentifier ];
45-
46- if (instancePID != currentPID) {
47- // Use forceTerminate because the other instance may be blocked in a modal dialog
48- [app forceTerminate ];
49- [NSThread sleepForTimeInterval: 0.3 ];
50- }
36+ - (void )dealloc {
37+ if (latestRVLinkURL != nil ) {
38+ [latestRVLinkURL release ];
39+ latestRVLinkURL = nil ;
5140 }
52-
53- return NO ;
41+ [super dealloc ];
5442}
5543
5644- (void )handleGetURLEvent : (NSAppleEventDescriptor *)event withReplyEvent : (NSAppleEventDescriptor *)replyEvent
5745{
5846 NSLog (@" *** RVLinkLauncher Apple Event handler called! ***" );
59-
60- // If we're showing a dialog, close it so we can show a new one with the new URL
61- if (currentAlert != nil ) {
62- [[NSApplication sharedApplication ] stopModal ];
63- [[currentAlert window ] close ];
64- urlProcessed = NO ;
65- currentAlert = nil ;
66- }
67-
47+
6848 @try {
6949 NSAppleEventDescriptor *directObjectDescriptor = [event paramDescriptorForKeyword: keyDirectObject];
7050 if (directObjectDescriptor != nil ) {
7151 NSString *url = [directObjectDescriptor stringValue ];
7252 if (url != nil && [url hasPrefix: @" rvlink://" ]) {
7353 NSLog (@" RVLinkLauncher received URL: %@ " , url);
74- [self processRVLinkURL: url];
54+
55+ // Track the most recent rvlink URL
56+ if (latestRVLinkURL != nil ) {
57+ [latestRVLinkURL release ];
58+ latestRVLinkURL = nil ;
59+ }
60+ latestRVLinkURL = [url copy ];
61+
62+ if (currentAlert != nil ) {
63+ // Update the existing dialog in place and bring it to the front
64+ [currentAlert setInformativeText: [NSString stringWithFormat: @" Opening: %@ " , latestRVLinkURL]];
65+ [[currentAlert window ] makeKeyAndOrderFront: nil ];
66+ } else {
67+ // No dialog is currently shown – process normally
68+ [self processRVLinkURL: latestRVLinkURL];
69+ }
70+
7571 urlProcessed = YES ;
76- // Exit app after processing URL
77- dispatch_after (dispatch_time (DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue (), ^{
78- [[NSApplication sharedApplication ] terminate: nil ];
79- });
8072 } else {
8173 NSLog (@" RVLinkLauncher received invalid or non-rvlink URL: %@ " , url);
8274 }
@@ -153,6 +145,16 @@ - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppl
153145
154146- (void )processRVLinkURL : (NSString *)rvlinkURL
155147{
148+ // Remember the URL we are currently processing so that if new URLs arrive
149+ // while a chooser dialog is visible, we can still open the most recent one.
150+ if (rvlinkURL != nil ) {
151+ if (latestRVLinkURL != nil ) {
152+ [latestRVLinkURL release ];
153+ latestRVLinkURL = nil ;
154+ }
155+ latestRVLinkURL = [rvlinkURL copy ];
156+ }
157+
156158 // Find all RV apps using both discovery methods
157159 NSLog (@" Discovering RV applications using multiple methods..." );
158160
@@ -204,7 +206,7 @@ - (void)processRVLinkURL:(NSString *)rvlinkURL
204206 NSString *displayName = [[NSFileManager defaultManager ] displayNameAtPath: [selectedAppURL path ]];
205207 NSLog (@" Only one RV application found, launching directly: %@ " , displayName);
206208
207- NSURL *targetURL = [NSURL URLWithString: rvlinkURL ];
209+ NSURL *targetURL = [NSURL URLWithString: latestRVLinkURL ];
208210 if (targetURL != nil ) {
209211 NSWorkspaceOpenConfiguration *config = [NSWorkspaceOpenConfiguration configuration ];
210212 [[NSWorkspace sharedWorkspace ] openURLs: @[targetURL]
@@ -215,19 +217,19 @@ - (void)processRVLinkURL:(NSString *)rvlinkURL
215217 NSLog (@" Failed to launch app: %@ " , [selectedAppURL path ]);
216218 NSLog (@" Error: %@ " , [err localizedDescription ]);
217219 } else {
218- NSLog (@" Successfully opened URL: %@ with app: %@ " , rvlinkURL , [selectedAppURL path ]);
220+ NSLog (@" Successfully opened URL: %@ with app: %@ " , latestRVLinkURL , [selectedAppURL path ]);
219221 }
220222 }];
221223 } else {
222- NSLog (@" Invalid rvlink URL: %@ " , rvlinkURL );
224+ NSLog (@" Invalid rvlink URL: %@ " , latestRVLinkURL );
223225 }
224226 return ;
225227 }
226228
227229 // Present chooser UI for multiple apps
228230 currentAlert = [[NSAlert alloc ] init ];
229231 [currentAlert setMessageText: @" Choose RV Application" ];
230- [currentAlert setInformativeText: [NSString stringWithFormat: @" Opening: %@ " , rvlinkURL ]];
232+ [currentAlert setInformativeText: [NSString stringWithFormat: @" Opening: %@ " , latestRVLinkURL ]];
231233 [currentAlert addButtonWithTitle: @" Open" ];
232234 [currentAlert addButtonWithTitle: @" Cancel" ];
233235
@@ -271,7 +273,7 @@ - (void)processRVLinkURL:(NSString *)rvlinkURL
271273
272274 if (result == NSAlertFirstButtonReturn ) {
273275 NSURL *selectedAppURL = [[popup selectedItem ] representedObject ];
274- NSURL *targetURL = [NSURL URLWithString: rvlinkURL ];
276+ NSURL *targetURL = [NSURL URLWithString: latestRVLinkURL ];
275277 if (targetURL != nil ) {
276278 NSWorkspaceOpenConfiguration *config = [NSWorkspaceOpenConfiguration configuration ];
277279 [[NSWorkspace sharedWorkspace ] openURLs: @[targetURL]
@@ -325,9 +327,6 @@ int main(int argc, const char * argv[])
325327 CFRelease (currentHandler);
326328 }
327329
328- // Terminate any other running instances to prevent multiple dialogs
329- [RVLinkURLHandler terminateOtherInstancesIfNeeded ];
330-
331330 // Check for command line URL
332331 NSString *rvlinkURL = nil ;
333332 for (int i = 1 ; i < argc; i++) {
@@ -338,10 +337,17 @@ int main(int argc, const char * argv[])
338337 }
339338 }
340339
341- // If a URL was provided via command line, process it immediately and exit
340+ // If a URL was provided via command line, forward it to LaunchServices and exit.
341+ // This avoids creating a second launcher instance with its own UI when the
342+ // binary is invoked directly from the terminal.
342343 if (rvlinkURL != nil ) {
343- NSLog (@" Processing rvlink URL from command line: %@ " , rvlinkURL);
344- [urlHandler processRVLinkURL: rvlinkURL];
344+ NSLog (@" Forwarding rvlink URL from command line to LaunchServices: %@ " , rvlinkURL);
345+ NSURL *url = [NSURL URLWithString: rvlinkURL];
346+ if (url != nil ) {
347+ [[NSWorkspace sharedWorkspace ] openURL: url];
348+ } else {
349+ NSLog (@" Invalid rvlink URL from command line: %@ " , rvlinkURL);
350+ }
345351 return 0 ;
346352 }
347353
0 commit comments