Modal event processing

  • I have a demonstration app that is basically operating on a 0.15 second timer loop. The problem I am having is that even with a periodic timer going off it is very difficult to get menu events and mouse clicks in window controls through to be processed. I have tried setting up an additional flow of events using
    (NSEvent) + (void)startPeriodicEventsAfterDelay:(NSTimeInterval)delaySecondswithPeriod:(NSTimeInterval)periodSeconds

    as well as

    [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: t != 0. ? t * 1.01 : .01]]

    (where t can be set arbitrarily but is typically called with a value of 0.15) but none of this improves response. Is there anything else to try?

    Charlie Dickman
    <3tothe4th...>
  • On 06/07/2012, at 5:57 AM, Charlie Dickman wrote:

    > I have a demonstration app that is basically operating on a 0.15 second timer loop. The problem I am having is that even with a periodic timer going off it is very difficult to get menu events and mouse clicks in window controls through to be processed. I have tried setting up an additional flow of events using
    > (NSEvent) + (void)startPeriodicEventsAfterDelay:(NSTimeInterval)delaySecondswithPeriod:(NSTimeInterval)periodSeconds
    >
    > as well as
    >
    > [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: t != 0. ? t * 1.01 : .01]]
    >
    > (where t can be set arbitrarily but is typically called with a value of 0.15) but none of this improves response. Is there anything else to try?

    Well, you could try writing your app correctly, rather than polling and running the event loop manually.

    What are you trying to do? Why can't you let the event loop run normally?

    --Graham
  • Correct, by your say so, or not if the timers don't drive the program it just sits there and does nothing. Human intervention is required to configure it or cause it to quit.

    On Jul 5, 2012, at 8:24 PM, Graham Cox wrote:

    >
    > On 06/07/2012, at 5:57 AM, Charlie Dickman wrote:
    >
    >> I have a demonstration app that is basically operating on a 0.15 second timer loop. The problem I am having is that even with a periodic timer going off it is very difficult to get menu events and mouse clicks in window controls through to be processed. I have tried setting up an additional flow of events using
    >> (NSEvent) + (void)startPeriodicEventsAfterDelay:(NSTimeInterval)delaySecondswithPeriod:(NSTimeInterval)periodSeconds
    >>
    >> as well as
    >>
    >> [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: t != 0. ? t * 1.01 : .01]]
    >>
    >> (where t can be set arbitrarily but is typically called with a value of 0.15) but none of this improves response. Is there anything else to try?
    >
    >
    > Well, you could try writing your app correctly, rather than polling and running the event loop manually.
    >
    > What are you trying to do? Why can't you let the event loop run normally?
    >
    >
    > --Graham

    Charlie Dickman
    <3tothe4th...>
  • On 06/07/2012, at 11:29 AM, Charlie Dickman wrote:

    > Correct, by your say so, or not if the timers don't drive the program it just sits there and does nothing. Human intervention is required to configure it or cause it to quit.

    What are you trying to DO?

    There's nothing wrong with using timers to "drive" some sort of intermittent activity, but by the sound of it you are trying to use a timer to drive the whole app. But unless you explain what you're trying to do, it's too vague to be able to help point you in the right direction.

    --Graham
  • Are you trying to mimic user interactions with Cocoa UI?
    If I understand you right, it might be easier to do it with accessibility API, so you can write a normal Cocoa App and a write another app to mimic user interaction to this cocoa app.

    -Jonny

    在 2012-7-6,上午3:57,Charlie Dickman <3tothe4th...> 写道:

    > I have a demonstration app that is basically operating on a 0.15 second timer loop. The problem I am having is that even with a periodic timer going off it is very difficult to get menu events and mouse clicks in window controls through to be processed. I have tried setting up an additional flow of events using
    > (NSEvent) + (void)startPeriodicEventsAfterDelay:(NSTimeInterval)delaySecondswithPeriod:(NSTimeInterval)periodSeconds
    >
    > as well as
    >
    > [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: t != 0. ? t * 1.01 : .01]]
    >
    > (where t can be set arbitrarily but is typically called with a value of 0.15) but none of this improves response. Is there anything else to try?
    >
    > Charlie Dickman
    > <3tothe4th...>
  • On 05.07.2012, at 21:57, Charlie Dickman wrote:
    > I have a demonstration app that is basically operating on a 0.15 second timer loop. The problem I am having is that even with a periodic timer going off it is very difficult to get menu events and mouse clicks in window controls through to be processed. I have tried setting up an additional flow of events using
    > (NSEvent) + (void)startPeriodicEventsAfterDelay:(NSTimeInterval)delaySecondswithPeriod:(NSTimeInterval)periodSeconds
    >
    > as well as
    >
    > [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: t != 0. ? t * 1.01 : .01]]
    >
    > (where t can be set arbitrarily but is typically called with a value of 0.15) but none of this improves response. Is there anything else to try?

    It's not how long you give time to the event loop, it is how often. Also, startPeriodicEventsAfterDelay: is intended for mouse tracking, not for general-purpose timers. You're being very vague and fuzzy with your terminology, so I hope you don't mind if I just spell out how I'd do a periodic task, and then you can tell whether that's what you're doing and when you're doing it differently:

    -> Use an NSTimer to get your periodic processing time, not other calls.
    -> Let the rest of the app run normally
    -> Do the minimum amount of work needed in your timer. Timers run on your main thread & run loop, so you have to do short bits of work so the OS can "come up for breath" often enough. E.g. do your pre-loop setup, then put the loop body in the timer (using instance variables for the counter & other stuff you need to keep around between iterations), and then do 1 iteration per timer fire.
    -> Don't use blocking calls (like network access) on the main run loop, use calls that do their work on another thread and then call you back when they're done (e.g. NSURLConnection, not NSURL -initWithContentsOfURL:).

    Or, if you can run your stuff on a secondary thread, queue up your little bits of work in an NSOperationQueue or other GCD-backed queue that runs on another thread.

    Cheers,
    -- Uli Kusterer
    "The Witnesses of TeachText are everywhere..."
  • Actually, no, I'm not trying to mimic user interaction but that's an idea worth thinking about.

    The program demonstrates how to play Chinese Mah-Jong as described in A.D. Millington's (excellent) book "The Complete Book of Mah-Jong so it runs on a timer driven dispatcher to have each player in turn play his hand.

    The problem I am having is that outside events like a click in the menu bar or a keyboard event have a hard time getting through.

    On Jul 5, 2012, at 11:19 PM, Yingshen Yu wrote:

    > Are you trying to mimic user interactions with Cocoa UI?
    > If I understand you right, it might be easier to do it with accessibility API, so you can write a normal Cocoa App and a write another app to mimic user interaction to this cocoa app.
    >
    > -Jonny
    >
    >
    > 在 2012-7-6,上午3:57,Charlie Dickman <3tothe4th...> 写道:
    >
    >> I have a demonstration app that is basically operating on a 0.15 second timer loop. The problem I am having is that even with a periodic timer going off it is very difficult to get menu events and mouse clicks in window controls through to be processed. I have tried setting up an additional flow of events using
    >> (NSEvent) + (void)startPeriodicEventsAfterDelay:(NSTimeInterval)delaySecondswithPeriod:(NSTimeInterval)periodSeconds
    >>
    >> as well as
    >>
    >> [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: t != 0. ? t * 1.01 : .01]]
    >>
    >> (where t can be set arbitrarily but is typically called with a value of 0.15) but none of this improves response. Is there anything else to try?
    >>
    >> Charlie Dickman
    >> <3tothe4th...>

    Charlie Dickman
    <3tothe4th...>
  • Uli,

    Thanks for the education. The way you suggest is what I did pre-OS X and Xcode/Cocoa and I didn't have this problem Being self taught (by books, sample code and documentation) in programming in Xcode/Cocoa for OS X and IOS it sounds like I started off on the wrong foot with the versatility of timers.

    I'll have to re-architect the app.

    Thanks again.

    On Jul 6, 2012, at 7:24 AM, Uli Kusterer wrote:

    > On 05.07.2012, at 21:57, Charlie Dickman wrote:
    >> I have a demonstration app that is basically operating on a 0.15 second timer loop. The problem I am having is that even with a periodic timer going off it is very difficult to get menu events and mouse clicks in window controls through to be processed. I have tried setting up an additional flow of events using
    >> (NSEvent) + (void)startPeriodicEventsAfterDelay:(NSTimeInterval)delaySecondswithPeriod:(NSTimeInterval)periodSeconds
    >>
    >> as well as
    >>
    >> [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: t != 0. ? t * 1.01 : .01]]
    >>
    >> (where t can be set arbitrarily but is typically called with a value of 0.15) but none of this improves response. Is there anything else to try?
    >
    > It's not how long you give time to the event loop, it is how often. Also, startPeriodicEventsAfterDelay: is intended for mouse tracking, not for general-purpose timers. You're being very vague and fuzzy with your terminology, so I hope you don't mind if I just spell out how I'd do a periodic task, and then you can tell whether that's what you're doing and when you're doing it differently:
    >
    > -> Use an NSTimer to get your periodic processing time, not other calls.
    > -> Let the rest of the app run normally
    > -> Do the minimum amount of work needed in your timer. Timers run on your main thread & run loop, so you have to do short bits of work so the OS can "come up for breath" often enough. E.g. do your pre-loop setup, then put the loop body in the timer (using instance variables for the counter & other stuff you need to keep around between iterations), and then do 1 iteration per timer fire.
    > -> Don't use blocking calls (like network access) on the main run loop, use calls that do their work on another thread and then call you back when they're done (e.g. NSURLConnection, not NSURL -initWithContentsOfURL:).
    >
    > Or, if you can run your stuff on a secondary thread, queue up your little bits of work in an NSOperationQueue or other GCD-backed queue that runs on another thread.
    >
    > Cheers,
    > -- Uli Kusterer
    > "The Witnesses of TeachText are everywhere..."
    >

    Charlie Dickman
    <3tothe4th...>
  • On 06/07/2012, at 11:38 PM, Charlie Dickman wrote:

    > I'll have to re-architect the app.

    Maybe.

    The thing that's key is that you do not run the event loop - it runs you (we're in Soviet Russia now). It also runs any timers which are attached to it. Timers do not (and can not) run the event loop - it's the other way around.

    Any time you feel tempted to "force" the event loop to run you have a big red flag (Russia again?) telling you something is wrong. If you look at the starter app that is created by Xcode, you will see no visible code that runs an event loop, but controls and menus you add to the basic xib work just fine.

    In the case of a game, if you have designed it so that the pieces can be moved by the user manually, adding automation shouldn't be too hard. You can use a timer to invoke movements at a lower level, just as the mouse events call down to cause movement at the same lower level. (With Core Animation and using a CALayer for each of the pieces, doing this with animation should be really easy, though the setup with CA would take a bit more time - look at the GeekGameBoard sample code, it's probably 90% of the necessary game mechanics as it is).

    --Graham
  • In a situation similar to yours, I ended up replacing the NSTimer with an NSAnimation. The animation I was doing was way faster after this tiny change, plus the user interface was not blocked at all. I refresh my animation 30 times per second now without any negative effect for the user. I think it is worth a try in your case before you rethink your application.

    [[[Brainchild alloc] initWithName:@"Richard Altenburg"] saysBestRegards];

    Op 6 jul. 2012, om 15:38 heeft Charlie Dickman het volgende geschreven:

    > I'll have to re-architect the app.
  • I just had a look at the NSAnimation class document and it actually suggests very prominently that, because of the limited capabilities of an NSAnimation, an NSTimer be used.

    I also just went back over the architecture of my app with Uli's comments in mind and have discovered that I think I am actually doing what he suggests. I am not polling for events nor am I trying to drive the run loop myself.

    In my view's drawRect method I use a global state variable that defines the next function to perform. When drawRect gets called I do what is necessary for the current state, set up for the next state, set the value for the next state and then return from drawRect. So the primary driving force is the periodic view update requirement (which is guaranteed by actions invoking [self setNeedsDisplay: YES]).

    Within the execution of a particular function I use NSTimers to drive the steps of that function ultimately exiting back to the dispatch stack in drawRect.

    So I am at a loss as how to better allow the system to do a better job of servicing menu clicks or keyboard presses.

    Can you help me to see what I am missing?

    On Jul 6, 2012, at 10:22 AM, Richard Altenburg (Brainchild) wrote:

    > In a situation similar to yours, I ended up replacing the NSTimer with an NSAnimation. The animation I was doing was way faster after this tiny change, plus the user interface was not blocked at all. I refresh my animation 30 times per second now without any negative effect for the user. I think it is worth a try in your case before you rethink your application.
    >
    >
    > [[[Brainchild alloc] initWithName:@"Richard Altenburg"] saysBestRegards];
    >
    > Op 6 jul. 2012, om 15:38 heeft Charlie Dickman het volgende geschreven:
    >
    >> I'll have to re-architect the app.
    >

    Charlie Dickman
    <3tothe4th...>
  • On 6 Jul 2012, at 3:02 PM, Charlie Dickman wrote:

    > In my view's drawRect method I use a global state variable that defines the next function to perform. When drawRect gets called I do what is necessary for the current state, set up for the next state, set the value for the next state and then return from drawRect. So the primary driving force is the periodic view update requirement (which is guaranteed by actions invoking [self setNeedsDisplay: YES]).
    >
    > Within the execution of a particular function I use NSTimers to drive the steps of that function ultimately exiting back to the dispatch stack in drawRect.
    >
    > So I am at a loss as how to better allow the system to do a better job of servicing menu clicks or keyboard presses.

    Taking my cue from "exiting back to the dispatch stack in drawRect" — I don't have the earlier parts of the thread before me, so forgive me if I make the wrong guesses about your intention.

    AppKit essentially requires that you use -drawRect: for drawing, and for no other processing at all. Otherwise you're turning your design inside-out ("fighting the framework"). Driving updates to state is the business of your controller layer (maybe the model), not the view layer.

    The controller handles an NSTimer firing.

    1) It sets properties in the view to configure how it will be drawn next, and calls -setNeedsDisplay:. The view draws. That's it.

    OR

    2) It updates the model layer. The model informs the controller through a notification or key-value observation that it has changed. The controller updates the view parameters, and calls -setNeedsDisplay:. The view draws. That's it.

    OR

    3) This is some sort of animation (a throb, for instance) that doesn't represent any model or controller state. Then the view might handle the timer, or use an NSAnimation, to update its own parameters, and call -setNeedsDisplay:. The view draws. That's it. Better: It uses Core Animation to do autonomous changes in rendering.

    In all cases, the event mechanism coalesces all the needs-display calls into a periodic call to -drawRect:. In -drawRect:, the view draws itself and does nothing else. The controller and the model do all the processing.

    And the event mechanism interleaves user events the whole time.

    — F
  • Fritz Anderson's comments are very important, in that you do not control the state of your animation in the drawRect: but instead handle that from a calling method (like the one in your controller which is called from your timer). drawRect: is not fully under your control nor should you try to do so.

    The documentation for animations states that for very simple work you should use the NSTimer, else use NSAnimation. I went for NSTimer first because I got so used to them in my previous development environment, but I was not happy with the fastest speed at which it would allow me to fire the timer, and they way it blocked my user interface. I swapped it for an NSAnimation, and it all worked beautifully after that. Mind you, I do not use full blown animation, I just abuse the NSAnimation as a timer, just like the NSTimer, but without the downsides.

    I am very new to Objective-C and Cocoa/Cocoa Touch, so you should listen to more advanced people on this list. But, I will tell you what I found to be effective and NSAnimation was very effective. And to see how I did this, I suggest you watch my small movie about this project:

    http://www.youtube.com/watch?v=jKg1Fy-LKVY

    [[[Brainchild alloc] initWithName:@"Richard Altenburg"] saysBestRegards];

    Op 6 jul. 2012, om 22:02 heeft Charlie Dickman het volgende geschreven:

    > I just had a look at the NSAnimation class document and it actually suggests very prominently that, because of the limited capabilities of an NSAnimation, an NSTimer be used.
  • On 07/07/2012, at 6:02 AM, Charlie Dickman wrote:

    > In my view's drawRect method I use a global state variable that defines the next function to perform. When drawRect gets called I do what is necessary for the current state, set up for the next state, set the value for the next state and then return from drawRect. So the primary driving force is the periodic view update requirement (which is guaranteed by actions invoking [self setNeedsDisplay: YES]).

    Hmmm, don't do that.

    -drawRect: is going to be called for all sorts of reasons, not just because your timer refreshed the view. It's quickly going to get out of step if your state machine (which is what it is) is advanced on every -drawRect:

    Abstract the state machine so its state can be changed by some explicit message rather than -drawRect:, then you can control when it changes state according to the logic of your game. Again this sounds upside-down, in that you're using the drawing to control the state instead of using the state to control the drawing.

    A naïve way to implement animation is to try to run a timer at, say 30 fps, which refreshes a view. When the view draws, it updates the animation state, for example by moving a graphic object some fixed distance. If things run smoothly, it "works", in that the speed and position of the object are apparently correct. Then sometimes they aren't, because for whatever reason the 30fps can't be maintained, the motion slows down and the position is less than what was predicted. The right way to do this is to model the physics in a simple way, remembering that speed is distance / time. So when your timer fires, you measure the elapsed time and use that to calculate the correct position of the object. You set its position there and you refresh the view. Then when it is drawn, it is in the right place. It does not matter how slow or fast the view refresh manages to be, it will animate correctly - the object speed is now independent of the view update rate. This is a subtle difference but crucial to success.

    You have to design your game and all its animation as if it were totally invisible. In other words, it has a data model that models the game in all its states, including transitional animations that has nothing to do with screens or views or pixels. Then you get your view to render that data model as a sort of 'snapshot' of what it's doing at any particular moment. It should not matter how quickly or slowly you take that snapshot - what you see is what the game is doing at that moment. If you take snapshots fast enough (30fps) you will see the game animated smoothly.

    If on the other hand you use the taking of the snapshot to drive the game, it will be terrible. Effectively your game is in a static state until someone looks at it, at which point you're trying to hurriedly rearrange it into the expected state for display. If that were real life, you can see the absurdity of it.

    You can use a timer to keep the state of the game updated, but at each moment, calculate the full state of the game and schedule a view update. Don't schedule a view update and use that to update the game state. If the logic is factored in a reasonable way, you can see that this amounts to swapping two lines of code. It makes all the difference.

    --Graham
previous month july 2012 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