Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runloop should be more flexible #6

Open
tim-weis opened this issue Jun 30, 2020 · 2 comments
Open

runloop should be more flexible #6

tim-weis opened this issue Jun 30, 2020 · 2 comments

Comments

@tim-weis
Copy link

This crate is awesome! I've tried virtually every native Windows windowing crate out there, and wasn't happy with any of them, for one reason or another. This one, though, is sweet: Well designed, documented in excruciating detail, concise, and embarrassingly tiny.

So tiny in fact, that I almost feel bad for proposing to make it larger. Anyway, here it goes:

As currently implemented, runloop's logic is fixed: It waits for a message to arrive, passes it on to an optional accelerator table, translates, and dispatches it. That works for a wide range of applications, but doesn't quite address a number common scenarios, namely the following three:

  1. A dialog-like window with automatic keyboard navigation.

    There's a hidden gem (?) in the Windows API, that gives you standard keyboard navigation for free: IsDialogMessageW. But it has to be called in your message loop (and it's a bit involved, too). That would be nice to have (if child window creation os on the road map).

  2. A UI thread that waits on messages and synchronization primitives.

    MsgWaitForMultipleObjects and MsgWaitForMultipleObjectsEx allow clients to fire off asynchronous operations and have them deliver status updates onto the UI thread. This is a common scenario, e.g. when using overlapped I/O. Again, this needs to be implemented in the message loop.

  3. A game-like loop with idle processing.

    The canonical message loop for games constantly polls for messages, and then moves on to do other calculations when the current batch of messages has been processed. In C this looks something like this:

    while (true) {
        while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
            TranslateMessage(&msg);
            DispatchMessageW(&msg);
        }
        // Perform other calculations
    }

    This scenario also isn't covered by runloop as currently implemented.

Though I don't have an immediate need for any of the above, I can see myself running into one of those limitations soon(-ish). Does this sound like something, that could be implemented in a future revision of this crate?

@raphlinus
Copy link
Owner

In druid-shell (back when it was xi-win) we had a bunch of that stuff, and made a deliberate decision to take it out. The fundamental problem is that the application is not always in control of its own runloop. The major cases are live resizing and popping up a modal window such as a file dialog.

I haven't researched your (1) yet. I think it does make sense to add child window creation, as, aside from this, we probably need it to do combo-box style widgets.

Regarding (2), I think the best way to do this is to post a message to the UI thread to wake it up. Otherwise, those waits get deferred while the app is not running its own runloop. Similar for (3). Depending on the application needs, the background processing should be in another thread (where it avoids all these messy reentrancy problems), or, if it's just small grains of work, sending a message to the window to request idle handling (this mechanism exists in druid-shell).

I can see that a game can ignore resizing and file dialogs, or have degraded behavior in those cases (not running the world simulation). I wouldn't reject a PR to add callbacks from the main runloop, but would strongly encourage potential users to consider other structures.

Thanks for your interest!

@tim-weis
Copy link
Author

tim-weis commented Jul 3, 2020

Thanks for taking the time to respond.

I understand that when the system fires up its own modal loop, messages temporarily won't reach the application's message loop. As a consequence, a hypothetical message loop based on MsgWaitForMultipleObjects might observe signaled synchronization objects later than is desirable. The alternative is worse, though, as it shifts the outcome from "deferred" to "missed". Raymond Chen wrote about the corollaries of posting thread messages: Thread messages are eaten by modal loops and Why do messages posted by PostThreadMessage disappear?.

Regarding (3) above: This type of message loop has a significant benefit, especially for games. It offers a natural way to synchronize certain types of operations, like input handling, physics processing, and rendering. This is not to say, that those operations are to be done on a single thread, just that they often need some sort of synchronization. From past experience, the thread responsible for input handling is usually in a distinguished position to orchestrate that synchronization in interactive real-time applications.

Though I think you are right. Once I need a more flexible message loop, I could probably try to put something together myself, and put it up for review. I'm curious, though, as I am not aware of potential reentrancy opportunities in a message loop that follows (3). Could you comment on this to keep me from wasting time on mistakes you have already discovered?

Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants