macOS: Embedded window support.#105884
Conversation
16778d8 to
3718ce1
Compare
2b57725 to
44ec980
Compare
IME activation and position events ( |
|
So far the only issues I have found, embedded view is not hiden when switching tabs from game tab (if floating window is diabled). And IME not workitng (expected since events are not passed). Screen.Recording.2025-04-29.at.11.26.29.mov |
That's great – I must have recently broken the tab switching, as I did fix that previously 🙄 Thanks for catching that, I'll push a fix! |
44ec980 to
e2527e4
Compare
|
@bruvzg I've fixed the panel hiding issue |
e2527e4 to
6d4ba50
Compare
stuartcarnie
left a comment
There was a problem hiding this comment.
Added some comments
There was a problem hiding this comment.
Various codecs for serialising and deserialising input events
There was a problem hiding this comment.
Note that debugger_marshalls.cpp have serialize_key_shortcut() method, which is something similar. The new codecs could replace that probably.
EDIT:
Also you are encoding things into binary data. You could just use var_to_bytes(), though it's probably less efficient (if that matters).
There was a problem hiding this comment.
Also you are encoding things into binary data. You could just use var_to_bytes(), though it's probably less efficient (if that matters).
Efficiency was a concern here, as we're sending a rapid stream of events to the child process, such as mouse movement. I did look at encoding variants but favoured a more efficient approach to keep latency to a minimum.
Note that debugger_marshalls.cpp have serialize_key_shortcut() method, which is something similar. The new codecs could replace that probably.
@KoBeWi I will move the encode / decode functions to DebuggerMarshalls. Wasn't aware of those, but it is an obvious place to put them. Thanks
There was a problem hiding this comment.
A small change to make polling a little more efficient.
There was a problem hiding this comment.
I moved the started signal to when the PID is set, as it was causing a race condition with the embedded process, which raised the started before it had received its PID, so it wasn't able to communicate with the child process
| #ifdef MACOS_ENABLED | ||
| extern "C" GameViewPluginBase *get_game_view_plugin(); | ||
| #else | ||
| GameViewPluginBase *get_game_view_plugin() { | ||
| return memnew(GameViewPlugin); | ||
| } | ||
| #endif | ||
|
|
There was a problem hiding this comment.
If there are recommendations for a better way to switch out the GameViewPlugin for macOS, I'm all ears.
There was a problem hiding this comment.
Maybe we can move this plugin to the DisplayServer entirely:
A plugin instance can be created and returned by the DisplayServer method. Windows and X11 overrides return base type. And macOS its special one.
There was a problem hiding this comment.
I will take a look at that - would be a nice approach
There was a problem hiding this comment.
This can be used for future implementations that want to serialise events across processes.
| - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; | ||
| - (void)accessibilityDisplayOptionsChange:(NSNotification *)notification; |
There was a problem hiding this comment.
Moved these into the mm so they are hidden from the public interface
| - (void)forceUnbundledWindowActivationHackStep1; | ||
| - (void)forceUnbundledWindowActivationHackStep2; | ||
| - (void)forceUnbundledWindowActivationHackStep3; |
There was a problem hiding this comment.
Moved these to the GodotApplication class, as I was exploring removing NSApplication loop completely from embedded child process, as it may not be needed.
| bool high_contrast; | ||
| bool reduce_motion; | ||
| bool reduce_transparency; | ||
| bool voice_over; |
There was a problem hiding this comment.
Moved these to the .mm file, as they don't need to be in the public interface.
| if (strcmp("--os-debug", argv[i]) == 0) { | ||
| i++; | ||
| wait_for_debugger = 5000; // wait 5 seconds by default | ||
| if (i < argc && strncmp(argv[i], "--", 2) != 0) { | ||
| wait_for_debugger = atoi(argv[i]); | ||
| i++; | ||
| } | ||
| continue; |
There was a problem hiding this comment.
For macOS, we can added a --os-debug <ms> run argument, so you can have it wait for a debugger and attach to debug the entire startup process when the Godot editor launches the child process, which was very handy for this work (and many other things I've done in the past)
There was a problem hiding this comment.
Probably should be in ifdef DEBUG_ENABLED block.
| String _get_default_fontname(const String &p_font_name) const; | ||
|
|
||
| static _FORCE_INLINE_ String get_framework_executable(const String &p_path); | ||
| static void pre_wait_observer_cb(CFRunLoopObserverRef p_observer, CFRunLoopActivity p_activiy, void *p_context); |
There was a problem hiding this comment.
Moved this to a block.
b14b6df to
affb92c
Compare
| #ifdef MACOS_ENABLED | ||
| extern "C" GameViewPluginBase *get_game_view_plugin(); | ||
| #else | ||
| GameViewPluginBase *get_game_view_plugin() { | ||
| return memnew(GameViewPlugin); | ||
| } | ||
| #endif | ||
|
|
There was a problem hiding this comment.
Maybe we can move this plugin to the DisplayServer entirely:
A plugin instance can be created and returned by the DisplayServer method. Windows and X11 overrides return base type. And macOS its special one.
| if (strcmp("--os-debug", argv[i]) == 0) { | ||
| i++; | ||
| wait_for_debugger = 5000; // wait 5 seconds by default | ||
| if (i < argc && strncmp(argv[i], "--", 2) != 0) { | ||
| wait_for_debugger = atoi(argv[i]); | ||
| i++; | ||
| } | ||
| continue; |
There was a problem hiding this comment.
Probably should be in ifdef DEBUG_ENABLED block.
editor/plugins/game_view_plugin.cpp
Outdated
| r_arguments.push_back("--rendering-driver"); | ||
| r_arguments.push_back("metal"); |
There was a problem hiding this comment.
Not sure about this, maybe it's better to check renderer and disable embedding if it's GL for now. Silently switching a running project to a different renderer is not what most users will expect.
4bf9a01 to
35cb8f7
Compare
|
@bruvzg OpenGL now works, so I've removed the explicit |
dd3bce0 to
604072c
Compare
|
You might've missed #105884 (comment) |
Thanks! |
💯 agree. I've made that change. |
|
Shoot, this got included in the merge batch erroneously. It should be fine as-is, but I meant to hold off for some more maintainer approvals & wait for the followup fixes. @stuartcarnie Those can go into an immediate followup PR, my apologies |
|
Heh, thankfully it had time to be well-reviewed and polished in the past couple of days, so it was 99% ready to merge already. Let's celebrate this huge achievement and long-awaited feature and not just the merge process mishap :P 🎉 Thanks for the amazing work @stuartcarnie 🥇 |
|
No stress, @Repiteo! I do have some follow up fixes that I'll prep today in a new PR! Thanks @akien-mga and to all those that reviewed. @Calinou my changes will include preventing embedding in single-window mode. |
|
Remaining items to investigate (no included in #106134):
|
macOS: Fix `template_debug` build after #105884


Embedded window support for macOS.
This feature is implemented using
CARemoteLayer, which is similar to how we implemented it for OpenEmu. This works by creating a CALayer in the child process and passing the CAContextID back to the Godot Editor. The editor is then able to create an instance ofCARemoteLayerand display the rendered output in real-time, as if it was hosted in the app. A major difference is that we serialise the events from the host app, the Godot Editor, and process them in the running game.The following additional state / events are processed correctly:
Joystick.mp4
Cursor.Shape.mp4
IME.mp4
OpenGL.and.more.mp4
⌥+ESCshortcut, always available to the editor, to release captureMouse.Capture.mp4