0

I'm writing a command line foundation tool in Mac OS X and would like the tool to quit on a keypress such as 'q'. The code is launching an asynchronous request for retrieving data from a remote server. This necessitates the NSRunLoop. At least that's what I understand I need to do.

Can someone tell me how to stop the runloop on the specific keypress?

Below is the code snippet.

int main (int argc, const char * argv[]) {

BOOL keepRunning = YES;
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

Requestor *myRequestor = [[Requestor alloc] init];
[myRequestor GetData];

NSRunLoop *runLoop;
runLoop = [NSRunLoop currentRunLoop];

while (keepRunning && [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);

[pool drain];
return 0;
}

Thank you!

BigBrother
  • 1,100
  • 1
  • 9
  • 17

3 Answers3

3

I haven't done this myself, but I would expect that you want to use [[NSFileHandle fileHandleWithStandardInput] readInBackgroundAndNotify] and register to receive the NSFileHandleReadCompletionNotification notification. If you receive a 'q', do what ever cleanup you need to and call exit().

Ken
  • 12,933
  • 4
  • 29
  • 32
0

Well, my initial theories and experiments starting with your existing code didn't turn up much in the way of usable code.

I'm imagining that what you're looking for is something like how, on Windows, you can run something in a command line shell window, and when the process has finished, it says something like "Press the Q key to continue...". When you bring the window forward (if it isn't already frontmost), and press the Q key, the window closes.

Are you planning on calling this command line tool from your primary application, or is this command line tool something the end-user will be interacting with directly? (For example, if the latter, they'd be calling it from a Terminal window, hmm, then I think Ken's code could probably be combined with mine to make the following. Note that in its current form, this only works after you press Q and then hit Return?

#import <Cocoa/Cocoa.h>

@interface Requestor : NSObject <NSApplicationDelegate> {
    BOOL            gotData;
    NSFileHandle    *stdIn;
}
- (void)getData;
- (void)requestorGotData:(id)sender;
@end

@implementation Requestor

- (id)init {
    if (self = [super init]) {
        gotData = NO;
        stdIn = nil;
        [NSApp setDelegate:self];
    }
    return self;
}

- (void)getData {
    NSLog(@"getting data.........");
    gotData = NO;

    [self performSelector:@selector(requestorGotData:)
               withObject:nil
               afterDelay:5.0];
}

break

- (void)requestorGotData:(id)sender {
    NSLog(@"got data");
    gotData = YES;
    NSLog(@"Press 'Q' key to continue...");
    stdIn = [[NSFileHandle fileHandleWithStandardInput] retain];
    [[NSNotificationCenter defaultCenter]
       addObserver:self
          selector:@selector(fileHandleReadCompletion:)
              name:NSFileHandleReadCompletionNotification
            object:stdIn];
    [stdIn readInBackgroundAndNotify];
}

- (void)fileHandleReadCompletion:(NSNotification *)notification {
    NSLog(@"fileHandleReadCompletion:");
    NSData *data = [[notification userInfo]
                    objectForKey:NSFileHandleNotificationDataItem];
    NSLog(@"data == %@", data);
    NSString *string = [[[NSString alloc]
                             initWithData:data
                        encoding:NSUTF8StringEncoding] autorelease];
    if (string) {
        string = [string stringByTrimmingCharactersInSet:
                  [NSCharacterSet whitespaceAndNewlineCharacterSet]];
        if ([[string lowercaseString] isEqualToString:@"q"]) {
            [stdIn closeFile];
            [stdIn release];
            [[NSNotificationCenter defaultCenter] removeObserver:self];
            [NSApp terminate:nil];
        } else {
            [stdIn readInBackgroundAndNotify];
        }
    }
}
@end

break

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    [NSApplication sharedApplication];

    Requestor *requestor = [[Requestor alloc] init];
    [requestor getData];

    [NSApp run];

    [requestor release];

    [pool drain];
    return 0;
}
NSGod
  • 22,699
  • 3
  • 58
  • 66
  • I was curious, so I compiled it (removing the Requestor lines). It builds and runs, but uses 100% CPU and hitting q does nothing ;) Do events get captured like that without a Window? Seems like libcurses is the standard way to handle this. – d11wtq Jan 06 '11 at 10:35
  • I think he wants a q on the stdin pipe, not a key press when the "app has focus". A command line program never has focus. Portable unix programs are certainly able to interact with the user at the keyboard. – Ken Jan 06 '11 at 19:09
  • @Ken: agreed. Tried fleshing your method out and it works, though in its current form, it requires the user to press after the letter Q.... – NSGod Jan 06 '11 at 20:13
0

If you haven't already considered libcurses, perhaps that will help you. It's really straightforward to catch keypresses with it, but what I'm not 100% about is if you can get it to work without the entire terminal window being used.

The curses bit alone is just:

#include <ncurses.h>

initscr();

/* snip */

char c;
while (c = getch()) {
  if (c == 'q') {
    // Put your cleanup and shutdown logic here
  }

  /* any other keypresses you might want to handle */
}

EDIT | You probably don't wanna put that tight loop inside your run loop... just call getch() each time the runloop ticks over.

d11wtq
  • 34,788
  • 19
  • 120
  • 195