18 KiB
Hosted Mode Window Management
Hosted mode is a GacUI operating mode in which the entire GUI application runs inside a single OS native window. Instead of creating one native window per GuiWindow, all sub-windows, dialogs, popups, and menus are rendered as graphics within the sole native window. The hosted mode implements its own window manager to handle z-ordering, hit testing, focus, activation, dragging, resizing, and input dispatching — functions normally provided by the OS.
The implementation lives in Source/PlatformProviders/Hosted/ with these files:
GuiHostedController.h/.cpp— the core controller wrapping a native controllerGuiHostedWindow.h/.cpp— virtual window implementingINativeWindowGuiHostedWindowManager.h— template-based window management algorithmGuiHostedGraphics.h/.cpp— graphics resource manager wrapperGuiHostedApplication.h/.cpp— global hosted application accessorGuiHostedWindowProxy_Main.cpp— proxy for the main windowGuiHostedWindowProxy_NonMain.cpp— proxy for non-main windowsGuiHostedWindowProxy_Placeholder.cpp— proxy for windows before role assignment
Entry Points and Activation
Hosted mode is activated through specific setup functions:
SetupHostedWindowsDirect2DRenderer()callsSetupWindowsDirect2DRendererInternal(true, false)SetupHostedWindowsGDIRenderer()callsSetupWindowsGDIRendererInternal(true, false)SetupRemoteNativeController(protocol)creates aGuiHostedControllerwrappingGuiRemoteController— remote mode inherently requires hosted mode
The internal setup functions (SetupWindowsDirect2DRendererInternal, SetupWindowsGDIRendererInternal) take a bool hosted parameter. When hosted is true:
StartWindowsNativeController(hInstance)creates the real native controller- A
GuiHostedControlleris created wrapping the native controller SetNativeController(hostedController)replaces the global native controllerSetHostedApplication(hostedController->GetHostedApplication())sets the hosted application globalRendererMainDirect2D/RendererMainGDIis called with the hosted controller- Inside the renderer main,
GuiHostedGraphicsResourceManagerwraps the native resource manager hostedController->Initialize()creates the single native windowGuiApplicationMain()runs the applicationhostedController->Finalize()tears down
For remote mode in SetupRemoteNativeController:
GuiRemoteControllerwraps the protocolGuiHostedControllerwraps theGuiRemoteController- Both remote and hosted resource managers are created in a chain
- The same initialize/run/finalize sequence applies
GuiHostedController Architecture
GuiHostedController is the central class of hosted mode. It inherits from:
Object— the GacUI base classhosted_window_manager::WindowManager<GuiHostedWindow*>— the window management algorithmINativeWindowListener— to listen to events on the single real native windowINativeControllerListener— to receive global timer, clipboard, and shortcut eventsINativeController— to replace the real native controller for the rest of GacUIINativeAsyncService— to redirect async operations through the real native controllerINativeScreenService/INativeScreen— to provide a virtual single screenINativeWindowService— to create, destroy, and manage virtual windowsIGuiHostedApplication— to expose the native window host
Service Delegation
The controller delegates most services to the underlying native controller:
ResourceService()→nativeController->ResourceService()ClipboardService()→nativeController->ClipboardService()ImageService()→nativeController->ImageService()InputService()→nativeController->InputService()DialogService()→ returnsnullptr(replaced byFakeDialogServiceBase)
Services it implements itself:
CallbackService()→ localSharedCallbackServiceinstanceAsyncService()→ itself (delegates internally to native async service, translates window references)ScreenService()→ itself (provides a single virtual screen)WindowService()→ itself (manages virtual hosted windows)
Virtual Screen
GuiHostedController acts as both INativeScreenService and INativeScreen:
GetScreenCount()returns 1GetScreen()returns itself- Screen bounds are derived from the native window's client size
- DPI scaling is forwarded from the real screen
Native Window Lifecycle
Initialize() creates one real native OS window via nativeController->WindowService()->CreateNativeWindow() and installs itself as INativeWindowListener on it.
Finalize() destroys the native window and uninstalls listeners.
Window Frame Configuration
GuiHostedController provides two frame configs:
GetMainWindowFrameConfig()— delegates to the native controller (the main window gets the real OS frame)GetNonMainWindowFrameConfig()— returns a static config withMaximizedBoxOption = AlwaysFalse,MinimizedBoxOption = AlwaysFalse,CustomFrameEnabled = AlwaysTrue(non-main windows are always custom-frame rendered)
Window Manager Template
WindowManager (GuiHostedWindowManager.h)
The core window management algorithm is template class hosted_window_manager::WindowManager<T> parameterized by window ID type. It maintains:
registeredWindows— dictionary of all registered windowsordinaryWindowsInOrder— z-ordered list of normal windows (front to back)topMostedWindowsInOrder— z-ordered list of top-most windows (front to back)mainWindow— the root windowactiveWindow— currently focused windowneedRefresh— dirty flag for rendering
Pure virtual callbacks used by GuiHostedController:
OnOpened/OnClosed— fireOpened/Closedevents on hosted window listenersOnEnabled/OnDisabled— fireEnabled/DisabledeventsOnGotFocus/OnLostFocus— fireGotFocus/LostFocuseventsOnActivated/OnDeactivated— fireActivated/Deactivatedevents
Window
Each Window<T> holds:
parent— parent window pointer forming a tree structurechildren— child windows listbounds— position and size in the parent's client coordinate spacetopMost,visible,enabled,active,renderedAsActive— state flags
Key operations:
SetParent(value)— reparents the window; null parent defaults to main window; updates z-order viaFixWindowInOrderSetVisible(bool)— shows or hides; triggersFixWindowInOrderandOnOpened/OnClosedcallbacksSetTopMost(bool)— toggles top-most status; moves window between ordinary and top-most z-order listsSetEnabled(bool)— enables or disables; triggers callbacksBringToFront()— moves window and its visible subtree to the front of z-order within its priority levelActivate()— sets focus; finds common parent with previously active window; updatesrenderedAsActivechain; callsBringToFrontDeactivate()— walks up parent chain to find next enabled window to activateShow()— shorthand forSetVisible(true)+Activate()
Z-Ordering
Two separate lists (ordinaryWindowsInOrder and topMostedWindowsInOrder) track window order. IsEventuallyTopMost() checks if a window or any ancestor is top-most and visible. FixWindowInOrder ensures windows are placed in the correct list based on their top-most status. EnsureChildrenMovedInFrontOf maintains the invariant that children appear before (in front of) their parent in the z-order list.
HitTest
WindowManager::HitTest(position) iterates topMostedWindowsInOrder then ordinaryWindowsInOrder, returning the first visible window whose bounds contain the position — giving top-most windows priority.
Start and Stop
WindowManager::Start(mainWindow) makes orphan windows children of the main window. WindowManager::Stop() clears all state and fires deactivation/close events.
GuiHostedWindow — Virtual INativeWindow
GuiHostedWindow implements INativeWindow and inherits from GuiHostedWindowData, which holds all window properties (title, icon, cursor, size state, border options, etc.). Each GuiHostedWindow contains a hosted_window_manager::Window<GuiHostedWindow*> wmWindow embedded in its data.
Proxy Pattern
GuiHostedWindow delegates behavior to an IGuiHostedWindowProxy. There are three proxy types:
- PlaceholderProxy (
GuiHostedWindowProxy_Placeholder.cpp) — initial no-op proxy for newly created windows before role assignment - MainProxy (
GuiHostedWindowProxy_Main.cpp,GuiMainHostedWindowProxy) — for the main window; delegates title, icon, size, border properties, maximized/minimized/show/hide to the real native window - NonMainProxy (
GuiHostedWindowProxy_NonMain.cpp,GuiNonMainHostedWindowProxy) — for all other windows; operates purely through the window manager; disallows maximized/minimized boxes; enforces custom frame mode when system borders are used
BecomeMainWindow() and BecomeNonMainWindow() switch the proxy. This happens in SettingHostedWindowsBeforeRunning() when the main window is known, and in CreateNativeWindow() for windows created after the main window.
Coordinate System
GetRenderingOffset()returns the window's top-left position in the native window's client space asNativeSizeGetBounds()returnswmWindow.boundsdirectly (position is in parent's client space)GetClientSize()equalsGetBounds().GetSize()(no frame for hosted windows)GetClientBoundsInScreen()equalsGetBounds()(screen = client area of native window)- DPI conversion functions delegate to the real native window
Capture and Cursor
RequireCapture()setscontroller->capturingWindowand callsnativeWindow->RequireCapture()ReleaseCapture()clearscontroller->capturingWindowand callsnativeWindow->ReleaseCapture()SetWindowCursor()only updates the real native window cursor if this is the currently hovering windowSetCaretPoint()adjusts byGetRenderingOffset()and sets on the real native window if this is the active window
Window Creation and Destruction
CreateNativeWindow(windowMode)
- Creates a
GuiHostedWindowwith placeholder proxy - Adds to
createdWindowssorted list - Registers with
wmManager - Fires
InvokeNativeWindowCreatedcallback - If main window already exists, calls
BecomeNonMainWindow()
DestroyNativeWindow(window)
- Clears references (
enteringWindow,hoveringWindow,capturingWindow, and WM-tracking pointers) - Fires
Destroyingon listeners - Fires
InvokeNativeWindowDestroyingcallback - Unregisters from
wmManager(which reparents children) - Removes from
createdWindows
Run(window)
- Sets
mainWindow - Calls
SettingHostedWindowsBeforeRunning()which:- Switches all windows to their proper proxies (main/non-main)
- Centers native window on screen
- Calls
wmManager->Start(&mainWindow->wmWindow)
- Delegates to
nativeController->WindowService()->Run(nativeWindow)for the message loop - Handles exceptions in unit test mode
- Calls
DestroyHostedWindowsAfterRunning()to tear down all hosted windows
Input Event Dispatching
Mouse Events
GuiHostedController listens to mouse events on the single native window. Mouse dispatching uses a template-based system:
HandleMouseCallback<PreAction, GetSelectedWindow, PostAction, Callback> is the core template:
- Calls
PreAction(may trigger window manager operations) - If not in a window-manager operation (
wmWindow == nullptr), callsGetSelectedWindowto find the target - Adjusts mouse coordinates by subtracting the target window's
wmWindow.boundstop-left - Dispatches to the target window's listeners
- Calls
PostAction
Three GetSelectedWindow strategies:
GetSelectedWindow_MouseDown— closes popup windows not in the hovering chain, returnscapturingWindoworhoveringWindowGetSelectedWindow_MouseMoving— updateshoveringWindowviaUpdateHoveringWindowandenteringWindowviaUpdateEnteringWindowGetSelectedWindow_Other— returnscapturingWindoworhoveringWindow
Individual mouse event methods (LeftButtonDown, MouseMoving, etc.) are wired to the right combination of PreAction/GetSelectedWindow/PostAction via IMPLEMENT_MOUSE_CALLBACK macros.
Window Manager Dragging and Resizing
PreAction_LeftButtonDown checks non-main enabled windows for hit test results (Title, BorderLeft, BorderRight, etc.). If a border/title hit is detected:
- Sets
wmOperationto the appropriateWindowManagerOperationenum value - Stores
wmWindowpointer andwmRelativeoffset - Captures the native window
PreAction_MouseMoving handles ongoing drag/resize when wmWindow is set: recalculates bounds based on mouse position and operation type, calls Moving and Moved listeners.
PostAction_LeftButtonUp checks for ButtonClose hit test to close windows, and ends window manager operations.
Mouse Hovering and Entering
UpdateHoveringWindow(location)stores mouse position and callsHitTestInClientSpaceto find the window under cursorUpdateEnteringWindow(window)firesMouseLeavedon the old entering window andMouseEnteredon the new oneMouseLeavedon the native window callsUpdateEnteringWindow(nullptr)
Mouse-Down Activation
PreAction_MouseDown activates the hovering window when a mouse button is pressed (if the window is enabled and activation-enabled).
Popup Closure on Click
GetSelectedWindow_MouseDown implements the pattern of closing popup windows (non-Normal mode windows) that are not ancestors of the clicked window.
Keyboard Events
HandleKeyboardCallback dispatches keyboard events (KeyDown, KeyUp, Char) to the active window's listeners.
Rendering Pipeline
Hosted Rendering in GlobalTimer
GuiHostedController::GlobalTimer() drives the rendering cycle. On each global timer tick:
- Skip if the native window is not visible or already in hosted rendering
- Check all visible windows' listeners for
NeedRefresh()— if any returns true, setneedRefresh - If no refresh is needed and nothing was updated last frame, skip rendering
- Enter rendering loop:
- Call
renderTarget->StartHostedRendering()on the native resource manager's render target - Iterate ordinary windows (back to front, reversed list order) then top-most windows
- For each window, call each listener's
ForceRefresh(false, updated, failureByResized, failureByLostDevice) - Call
renderTarget->StopHostedRendering() - Handle failures:
LostDevice→RecreateRenderTarget;ResizeWhileRendering→ResizeRenderTarget - On success, call
nativeWindow->RedrawContent()and break
- Call
Hosted Rendering Protocol (GuiGraphicsRenderTarget)
GuiGraphicsRenderTarget (base class for D2D/GDI render targets) supports hosted rendering:
StartHostedRendering()setshostedRendering = trueand callsStartRenderingOnNativeWindow()once- During hosted rendering, individual
StartRendering()/StopRendering()pairs do NOT callStartRenderingOnNativeWindow()/StopRenderingOnNativeWindow()— they just toggle therenderingflag StopHostedRendering()callsStopRenderingOnNativeWindow()once- This means all hosted windows render within a single begin/end rendering session on the native render target
Per-Window Rendering Offset (GuiGraphicsHost)
GuiGraphicsHost::Render() (called from ForceRefresh) applies the rendering offset:
hostRecord.renderTarget->StartRendering()auto nativeOffset = hostRecord.nativeWindow->GetRenderingOffset()— for hosted windows this returns the window positionauto localOffset = hostRecord.nativeWindow->Convert(nativeOffset)— converts to logical pixelswindowComposition->Render(localOffset)— renders the composition tree at the offsethostRecord.renderTarget->StopRendering()
The rendering offset mechanism is the key: in non-hosted mode, GetRenderingOffset() returns (0,0) because each window has its own render target. In hosted mode, it returns the window's position in the shared native window coordinate space, so all window content is rendered at the correct position within the single render target.
GuiHostedGraphicsResourceManager
GuiHostedGraphicsResourceManager wraps the native resource manager:
GetRenderTarget(window)always returns the native render target for the single native window, regardless of which hosted window asksRecreateRenderTarget(window)andResizeRenderTarget(window)are no-ops (managed by the global timer loop)- All other methods (element registration, renderer factories, layout provider) delegate to the native manager
Native Window Event Forwarding
GuiHostedController as INativeWindowListener on the single real native window forwards events to hosted windows:
HitTest(location)— if main window is enabled and cursor is on it, performs hit test via main window's listenersMoving(bounds)— adjusts bounds to main window's coordinate space and forwards to main window's listenersMoved()— syncs main window bounds to native window client sizeDpiChanged(preparing)— recreates render target and broadcasts to all hosted windowsGotFocus()— reactivates last focused window (or main window)LostFocus()— stores last focused window and deactivates allBeforeClosing(cancel)/AfterClosing()— forwards to main window's listenersOpened()— forwards to main window's listeners
Dialog Service
GuiHostedController::DialogService() returns nullptr, causing GacUI to use FakeDialogServiceBase — GacUI-implemented dialogs rendered inside the hosted environment. This is critical because OS-native dialogs would create separate windows outside the hosted environment.
FakeDialogServiceBase provides view models (IMessageBoxDialogViewModel, IColorDialogViewModel, IFontDialogViewModel, IOpenFileDialogViewModel, ISaveFileDialogViewModel) and creates corresponding GacUI windows that appear as hosted sub-windows.
Remote Mode Integration
Remote mode (SetupRemoteNativeController) always uses hosted mode:
GuiRemoteControllerimplementsINativeControllerthrough protocol communicationGuiHostedControllerwrapsGuiRemoteControllerjust as it wraps Windows native controllersGuiRemoteGraphicsResourceManagerwrapsGuiRemoteController, thenGuiHostedGraphicsResourceManagerwraps that- The single native window in remote mode is the
GuiRemoteWindow, which sends rendering commands through the protocol rather than to a real screen