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

[windows] Allow delegation of top-level WindowProc #20613

Merged

Conversation

stuartmorgan
Copy link
Contributor

Description

Adds APIs for runners to delegate WindowProc handlers into the Flutter
engine, and for plugins to register as possible delegates.

This allows for plugins to alter top-level window behavior in ways that
can only be done from the WindowProc, such as resize control. This
functionality remains entirely on the native side, so is synchronous.

Related Issues

Part of flutter/flutter#53168

Tests

I added the following tests: Unit tests for the registration and dispatch objects at both the wrapper and implementation levels.

Checklist

Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes ([x]). This will ensure a smooth and quick review process.

  • I read the contributor guide and followed the process outlined there for submitting PRs.
  • I signed the CLA.
  • I read and followed the C++, Objective-C, Java style guides for the engine.
  • I read the tree hygiene wiki page, which explains my responsibilities.
  • I updated/added relevant documentation.
  • All existing and new tests are passing.
  • I am willing to follow-up on review comments in a timely manner.

Breaking Change

Did any tests fail when you ran them? Please read handling breaking changes.

  • No, no existing tests failed, so this is not a breaking change.
  • Yes, this is a breaking change.

Adds APIs for runners to delegate WindowProc handlers into the Flutter
engine, and for plugins to register as possible delegates.

This allows for plugins to alter top-level window behavior in ways that
can only be done from the WindowProc, such as resize control. This
functionality remains entirely on the native side, so is synchronous.

Part of flutter/flutter#53168
Copy link
Member

@chinmaygarde chinmaygarde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some nits but lgtm otherwise.

std::optional<LRESULT> FlutterViewController::HandleTopLevelWindowProc(
HWND hwnd,
UINT message,
WPARAM wparam,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a nit but can we give these parameters better names? wparam and lparam is a bit terse. Same elsewhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really; this is just how WindowProc works. This is what they are called in the Win32 APIs, and the entire description for both is:

Additional message information. The contents of this parameter depend on the value of the uMsg parameter.

Because the meaning is wildly different depending on the message, there's no meaningful name to give them, and anything other than the names that are always used for them in WindowProc code would just be more confusing.

LRESULT result;
bool handled = FlutterDesktopViewControllerHandleTopLevelWindowProc(
controller_, hwnd, message, wparam, lparam, &result);
return handled ? result : std::optional<LRESULT>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: std::nullopt there please. I am never sure whether this means the same same as std::nullopt or an optional with a default initialized value. Maybe that's just me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default constructor is empty; it's the same as nullopt. It seems logical to me (just as, e.g., a std::unique_ptr's default constructor is empty, not a default-initialized object of the pointer's type), but I'm fine with making it explicit.

// - The application may choose not to delegate WindowProc calls.
// - If multiple plugins are registered, the first one that returns a value
// from the delegate message will "win", and others will not be called.
// The order of delegate calls is not defined.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like an unnecessary restriction that can easily be lifted if the Compare template parameter for the map below was specified. Saying either the first or last one wins ought to be a much less surprising API behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not that simple. Plugins on Windows are DLLs (to minimize the possibility of problematic interactions), so in practice each plugin has its own instance of this class, and changing the sort behavior here would do nothing (unless a single plugin registered multiple delegates, which is possible, but unlikely). It's actually the sort order in the engine that would matter most of the time. I could define it there, but then there are bizarre edge cases like: plugin A registers a handler, then plugin B registers a handler, then plugin A registers another handler. Without retooling the API to be more complex just to handle that case, the order of calls would actually be A1 A2 B, not A1 B A2.

I also don't really see that there's any value in such a guarantee, because in practice "first" or "last" aren't really under the control of a plugin developer. Assuming people will generally register this on plugin instantiation, that means that if we make this fully defined it devolves to the order of plugin registration. That's not under the control of a plugin developer, and in practice we don't make any guarantees about that. And even if we did (which I actually filed a bug about, because it's annoying if generated code that's checked in changes for no good reason, which happened recently), then we're basically saying that WindowProc handling is decided by alphabetical ordering of plugin names, which is weird, and could have the effect of people picking odd names for plugins just to try to win conflicts.

Assuming anyone actually tested the interaction between their plugin and other plugins, which I think is going to be very rare at best.

tl;dr: It's non-trivial added complexity, and I don't think it has any real value.

@stuartmorgan stuartmorgan merged commit dcbe3d3 into flutter:master Aug 19, 2020
@stuartmorgan stuartmorgan deleted the top-level-window-proc-delegation branch August 19, 2020 13:49
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Aug 19, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants