0

I have a Xcode 5/Cocoa program that clicks the left mouse button after specified interval a specified number of times. That part works fine. The problem occurs when I want to stop the while loop prematurely.

I'm using a listener to detect any key press during the running of the program, set a stopnow variable and check for that variable in the while loop. But, the while loop doesn't detect the change in the variable until the loop finishes.

Also, I change a counter in the title bar of the window to display the count of clicks done, and that doesn't get updated either until the loop finishes.

I do get the NSLog message when I press a key.

I'm very confused.

My code is here :

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 
    // Insert code here to initialize your application

[[self myWindow] setLevel:NSFloatingWindowLevel];

[NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask handler:^(NSEvent *event) {
    keychar = (unichar) event.characters;
    [NSApp activateIgnoringOtherApps:YES];
    stopnow = 1;
    NSLog(@"Key Pressed = x%x (%x) (%x)",keychar,(keychar&0x7f00),((keychar&0xff00)>>8));
 }];
}
- (IBAction)setClickPoint:(NSButton *)sender { 
     sleep(5); 
     CGEventRef ourEvent = CGEventCreate(NULL); 
     cgPoint = CGEventGetLocation(ourEvent);

     myPoint = [NSString stringWithFormat:@" (%5.0f,%5.0f)", cgPoint.x, cgPoint.y];
     myNewTitle = [mytitle stringByAppendingString:myPoint];
     [[self myWindow] setTitle:myNewTitle];
  }

(IBAction)strtButton:(NSButton *)sender { 
    NSLog(@"Entered strButtn"); 
    numClicks = [_nClicks intValue]; 
    numWait = [_nWait floatValue]; 
    i = 0;  
    while (i < numClicks || numClicks == 0) { 
        i++; 
        myTotal = [NSString stringWithFormat:@" %i of %i", i, numClicks]; 
        myNewTitle = [mytitle stringByAppendingString:myPoint]; 
        myNewTitle = [myNewTitle stringByAppendingString:myTotal];

        [[self myWindow] setTitle:myNewTitle];

        CGWarpMouseCursorPosition(cgPoint);

        CGEventRef down = CGEventCreateMouseEvent(0, kCGEventLeftMouseDown,cgPoint, 0);
        CGEventPost(kCGSessionEventTap, down);
        CFRelease(down);

        CGEventRef up = CGEventCreateMouseEvent(0, kCGEventLeftMouseUp,cgPoint, 0);
        CGEventPost(kCGSessionEventTap, up);
        CGRealease(up);

        NSLog(@"stopnow = %i", stopnow);

        if (stopnow == 1) {
            stopnow = 0;
            break;
        }

        usleep((unsigned int)(numWait * 1000000.0));
    }
}
Scotty.NET
  • 12,533
  • 4
  • 42
  • 51
user3581648
  • 161
  • 1
  • 3

2 Answers2

0

A Cocoa/Cocoa Touch app is an event-based environment, so you cannot have long running "loops" in the main thread, as you stop the handling and delivery of the events.

When your loop finishes, the UI is able to update the bits you are seeing, as it can now deliver the events.

You will need to do this work in the background thread, or some such.

Droppy
  • 9,691
  • 1
  • 20
  • 27
  • Would the anonymous coward, who downvoted, like to explain why? – Droppy Apr 30 '14 at 19:57
  • so - are you saying that I need to make the loop another method and create a thread with that method? If so, how would the new thread notify the main thread that it is finished? maybe a Callback method? – user3581648 Apr 30 '14 at 19:59
  • Yeah, any UI updates can be done via GCD, dispatching to the main queue. – Droppy Apr 30 '14 at 20:00
  • Thanks, I'll research GCD, this is my 1st attempt at programming on the Mac, the nomenclature is killing me. – user3581648 Apr 30 '14 at 20:11
  • @user3581648 it can be tricky when you're starting out. Here's the call you need for UI updates. http://stackoverflow.com/questions/5662360/gcd-to-perform-task-in-main-thread – Woodstock Apr 30 '14 at 20:42
  • So, no joy here, problem is that a GCD thread also doesn't get the current value of a global variable either. How can I terminate a thread prematurely? – user3581648 May 01 '14 at 17:44
0

Ok, here is what works - use dispatch_async(global type) for the main loop, use dispatch_async(main queue) for the code that updates the title.

user3581648
  • 161
  • 1
  • 3