Receiving user events from within an NSTimer callback

  • Hello all,

    I'm working on a Cocoa application which does all of its work within
    an NSTimer callback that is called regularly.  Events are handled
    normally within a Cocoa run loop.

    However, there are times when within the callback, the code enters an
    inner loop, and will not exit until some user action is performed.
    I'm trying to force the run loop to pump events within this inner
    loop, but I can't seem to ever get it to work.

    I've tried calling the following within the inner loop:
    - calling [[NSRunLoop currentRunLoop] runUntilDate:[NSDate
    dateWithTimeIntervalSinceNow:0.1]];
    - calling [[NSRunLoop mainRunLoop] runUntilDate:[NSDate
    dateWithTimeIntervalSinceNow:0.1]];
    - calling CFRunLoopRunInMode()
    - calling ReceiveNextEvent

    None of these let me get any events from within my callback.  Is there
    any way for me to do what I need?

    Thanks for any help!
    -john
  • On Jan 22, 2009, at 14:55, John Mikros wrote:

    > I'm working on a Cocoa application which does all of its work within
    > an NSTimer callback that is called regularly.  Events are handled
    > normally within a Cocoa run loop.
    >
    > However, there are times when within the callback, the code enters
    > an inner loop, and will not exit until some user action is performed.
    > I'm trying to force the run loop to pump events within this inner
    > loop, but I can't seem to ever get it to work.

    I use, from the timer callback:

    > NSEvent *event;
    > while (event = [NSApp nextEventMatchingMask: NSAnyEventMask
    > untilDate: nil inMode: NSEventTrackingRunLoopMode dequeue: YES])
    > [NSApp sendEvent: event];

    I don't know if this is officially blessed or not, but it certainly
    seems to work just fine.

    In the places I've done this, I was only really interested in making
    the mouse and keyboard work, hence NSEventTrackingRunLoopMode. You
    could use the default run loop mode instead, I guess.
  • On Thu, Jan 22, 2009 at 7:34 PM, Quincey Morris
    <quinceymorris...> wrote:
    > On Jan 22, 2009, at 14:55, John Mikros wrote:
    >
    >> I'm working on a Cocoa application which does all of its work within an
    >> NSTimer callback that is called regularly.  Events are handled normally
    >> within a Cocoa run loop.
    >>
    >> However, there are times when within the callback, the code enters an
    >> inner loop, and will not exit until some user action is performed.
    >> I'm trying to force the run loop to pump events within this inner loop,
    >> but I can't seem to ever get it to work.
    >
    > I use, from the timer callback:
    >
    >> NSEvent *event;
    >> while (event = [NSApp nextEventMatchingMask: NSAnyEventMask
    >> untilDate: nil inMode: NSEventTrackingRunLoopMode dequeue: YES])
    >> [NSApp sendEvent: event];
    >
    >
    > I don't know if this is officially blessed or not, but it certainly seems to
    > work just fine.
    >
    > In the places I've done this, I was only really interested in making the
    > mouse and keyboard work, hence NSEventTrackingRunLoopMode. You could use the
    > default run loop mode instead, I guess.

    Yes, this is fine, and supported. It's a common way of implementing
    mouse tracking instead of relying on the stateless mouseDragged:
    method.

    The reason you have to do this and can't just run the runloop is
    because the event loop is actually a separate concept from the
    runloop. It's a bit confusing because the event loop is implemented
    using the runloop, but if you just run the runloop on the main thread,
    events won't get processed. You have to explicitly run this in order
    to get them to be processed.

    Note that using the default runloop mode with this is generally a bad
    idea. By running in the default mode you allow *everything* else to
    run, which means that some other code might decide that *it* wants an
    inner event loop as well. If you then want to quit before that other
    code has finished, tough cookies. Much better to control things more
    tightly by using a different runloop mode.

    Mike
  • Thanks so much for the responses!  Indeed calling
    nextEventMatchingMask... and sending the event manually worked.

    Interesting info about the event loop being separate from (but linked
    to) the runloop.

    Thanks again -- this was a lifesaver :)

    On Jan 22, 2009, at 9:02 PM, Michael Ash wrote:

    > On Thu, Jan 22, 2009 at 7:34 PM, Quincey Morris
    > <quinceymorris...> wrote:
    >> On Jan 22, 2009, at 14:55, John Mikros wrote:
    >>
    >>> I'm working on a Cocoa application which does all of its work
    >>> within an
    >>> NSTimer callback that is called regularly.  Events are handled
    >>> normally
    >>> within a Cocoa run loop.
    >>>
    >>> However, there are times when within the callback, the code enters
    >>> an
    >>> inner loop, and will not exit until some user action is performed.
    >>> I'm trying to force the run loop to pump events within this inner
    >>> loop,
    >>> but I can't seem to ever get it to work.
    >>
    >> I use, from the timer callback:
    >>
    >>> NSEvent *event;
    >>> while (event = [NSApp nextEventMatchingMask: NSAnyEventMask
    >>> untilDate: nil inMode: NSEventTrackingRunLoopMode dequeue: YES])
    >>> [NSApp sendEvent: event];
    >>
    >>
    >> I don't know if this is officially blessed or not, but it certainly
    >> seems to
    >> work just fine.
    >>
    >> In the places I've done this, I was only really interested in
    >> making the
    >> mouse and keyboard work, hence NSEventTrackingRunLoopMode. You
    >> could use the
    >> default run loop mode instead, I guess.
    >
    > Yes, this is fine, and supported. It's a common way of implementing
    > mouse tracking instead of relying on the stateless mouseDragged:
    > method.
    >
    > The reason you have to do this and can't just run the runloop is
    > because the event loop is actually a separate concept from the
    > runloop. It's a bit confusing because the event loop is implemented
    > using the runloop, but if you just run the runloop on the main thread,
    > events won't get processed. You have to explicitly run this in order
    > to get them to be processed.
    >
    > Note that using the default runloop mode with this is generally a bad
    > idea. By running in the default mode you allow *everything* else to
    > run, which means that some other code might decide that *it* wants an
    > inner event loop as well. If you then want to quit before that other
    > code has finished, tough cookies. Much better to control things more
    > tightly by using a different runloop mode.
    >
    > Mike
previous month january 2009 next month
MTWTFSS
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
Go to today