mirror of
https://github.com/vczh-libraries/Release.git
synced 2026-08-17 17:31:44 +08:00
Sync coding agent knowledge base
This commit is contained in:
@@ -16,7 +16,7 @@ Testing GacUI applications without real OS windows or rendering, using the remot
|
||||
- Use `OnNextIdleFrame(name, callback)` on `UnitTestRemoteProtocol` to register flat frame callbacks; the name describes the already-captured rendering result, and the callback should perform an observable UI-changing action.
|
||||
- Use `LocationOf(controlOrComposition)` to compute absolute screen coordinates for input simulation.
|
||||
- Use `LClick`, `RClick`, `MClick`, `LDBClick`, `MouseMove` for mouse input simulation.
|
||||
- Use `KeyPress`, `KeyDown`, `KeyUp`, `TypeString` for keyboard input simulation.
|
||||
- Use `KeyPress`, `_KeyDown`, `_KeyDownRepeat`, `_KeyUp`, and `TypeString` for keyboard input simulation.
|
||||
- Use `TryFindObjectByName<T>(window, name)` to look up named controls from GacUI XML resources.
|
||||
- Use `GetApplication()->InvokeInMainThread` to defer IO actions that would trigger blocking functions like `ShowDialog`.
|
||||
|
||||
@@ -30,11 +30,12 @@ Testing GacUI applications without real OS windows or rendering, using the remot
|
||||
- The initialization process follows a layered architecture from platform entry points through renderer setup to application framework. It supports:
|
||||
- Windows Direct2D/GDI
|
||||
- Linux GTK
|
||||
- Wayland/WGac
|
||||
- macOS Cocoa
|
||||
- remote rendering for testing
|
||||
- hosted mode for embedded applications.
|
||||
- The system uses a consistent naming pattern `Setup[Platform][Renderer][Mode]()` with standard mode providing full application framework, hosted mode running within a single native window, and raw mode bypassing GuiApplication entirely.
|
||||
- Key features include hardware acceleration fallbacks, comprehensive error handling, frame-based unit testing through remote mode, and systematic service registration with proper dependency management.
|
||||
- Entry-point names are platform-specific: Windows uses `SetupWindows*`, `SetupHostedWindows*`, and `SetupRawWindows*`; macOS and WGac expose standard and hosted variants; GTK, remote mode, and code generation use their own setup names.
|
||||
- Key features include hardware acceleration fallbacks, comprehensive error handling, frame-based unit testing through remote mode, and systematic native-controller service provisioning.
|
||||
|
||||
[Design Explanation](./KB_GacUI_Design_PlatformInitialization.md)
|
||||
|
||||
@@ -63,9 +64,9 @@ Testing GacUI applications without real OS windows or rendering, using the remot
|
||||
|
||||
- Core layout system centered on `GuiGraphicsComposition` with measurement (`Layout_CalculateMinSize`) and arrangement (`Layout_CalculateBounds`) passes driven only by host render loop invalidation.
|
||||
- Three subclass archetypes (`_Trivial`, `_Controlled`, `_Specialized`) define ownership of size calculation and parent-child constraint propagation.
|
||||
- Eight predefined layout families (Bounds, Table, Stack, Flow, Shared Size, Side Aligned, Partial View, Window Root) plus responsive (`GuiResponsive*`) adaptive level-based system.
|
||||
- Nine predefined layout families (Bounds, Table, Stack, Flow, Shared Size, Side Aligned, Partial View, Window Root, Repeat) plus the responsive (`GuiResponsive*`) adaptive level-based system.
|
||||
- Bidirectional constraints: parent supplies space; children optionally enlarge parent via `Layout_CalculateMinClientSizeForParent`; controlled items receive bounds from parent setters.
|
||||
- Invalidation via `InvokeOnCompositionStateChanged`; `GuiGraphicsHost::Render` iteratively recalculates until stable; `ForceCalculateSizeImmediately` only for interactive latency.
|
||||
- Invalidation via `InvokeOnCompositionStateChanged` requests rendering; host layout and rendering converge across successive cycles; `ForceCalculateSizeImmediately` is reserved for interactive latency.
|
||||
- Responsive compositions add multi-level adaptive switching with aggregation strategies (View, Stack, Group, Fixed) and automatic container adjustment.
|
||||
|
||||
[Design Explanation](./KB_GacUI_Design_LayoutAndGuiGraphicsComposition.md)
|
||||
@@ -75,8 +76,8 @@ Testing GacUI applications without real OS windows or rendering, using the remot
|
||||
- Three-layered architecture from composition focus (`GuiGraphicsHost`) through control focus (`GuiControl`) to automatic clearing on state changes.
|
||||
- TAB navigation managed by `GuiTabActionManager` with `IGuiTabAction` service, prioritized control list building, wrapping navigation logic, and character suppression.
|
||||
- ALT navigation managed by `GuiAltActionManager` with `IGuiAltAction` service, nested ALT host hierarchy (`IGuiAltActionHost`), visual label creation, and prefix-based key filtering.
|
||||
- Critical `continue` barrier in `CollectAltActionsFromControl` blocks children from parent-level collection when control has ALT action, enabling nested context pattern.
|
||||
- Custom `GetActivatingAltHost` implementations handle non-child relationships (menu popups), intentional blocking (combo boxes), dynamic content (grid editors), and scoped navigation (ribbon groups, date pickers).
|
||||
- The `continue` barriers in `CollectAltActionsFromControl` always stop descent at an ALT-action container, and stop at a single ALT action only when it is available and enabled, preserving nested contexts.
|
||||
- Custom `GetActivatingAltHost` implementations handle non-child relationships (menu popups), intentional blocking (combo boxes), dynamic content (grid editors whose focus control exposes an available and enabled ALT action), and scoped navigation (ribbon groups, date pickers).
|
||||
- Event flow integration processes ALT before TAB in key event chain, with character suppression for both managers in character event chain.
|
||||
|
||||
[Design Explanation](./KB_GacUI_Design_ControlFocusSwitchingAndTabAltHandling.md)
|
||||
@@ -98,9 +99,9 @@ Testing GacUI applications without real OS windows or rendering, using the remot
|
||||
#### Adding a New Control
|
||||
|
||||
- Coordinated changes across control class definition, template system, theme management, reflection registration, and XML compiler integration.
|
||||
- Control class inherits from `GuiControl` and `Description<T>`, specifies template type via macro, overrides lifecycle methods, attaches event handlers to `boundsComposition`, and defines events/properties.
|
||||
- A control class inherits from `GuiControl` and `Description<T>`, specifies its template with `GUI_SPECIFY_CONTROL_TEMPLATE_TYPE`, defines the required underscore template hooks used by the macro-generated lifecycle overrides, attaches event handlers to `boundsComposition`, and defines events/properties.
|
||||
- Template system with declaration in `GuiControlTemplates.h` using macro expansion, property definition macros, auto-generated implementations including getters/setters/change events.
|
||||
- Inheritance pattern for derived controls using parent template as base, selective lifecycle override, and attachment to parent events instead of re-implementing handlers.
|
||||
- Derived controls use the parent template as a base, define both underscore template hooks even when empty, optionally override feature hooks such as `OnParentLineChanged`, `OnActiveAlt`, or `IsTabAvailable`, and attach to parent events instead of re-implementing handlers.
|
||||
- Reflection registration in three steps: type list addition, control class registration with base/constructor/members, automatic template registration.
|
||||
- XML loader registration via `ADD_TEMPLATE_CONTROL` or `ADD_VIRTUAL_CONTROL` for themed variants.
|
||||
- Theme integration through `GUI_CONTROL_TEMPLATE_TYPES` macro generating `ThemeName` enum values.
|
||||
@@ -127,8 +128,8 @@ Testing GacUI applications without real OS windows or rendering, using the remot
|
||||
- Remote protocol mode separates GacUI into a core side (application logic) and a renderer side (rendering and OS services), communicating through `IGuiRemoteProtocol` over Parser2 JSON channel packages.
|
||||
- Messages flow core → renderer via `IGuiRemoteProtocolMessages`; events and responses flow renderer → core via `IGuiRemoteProtocolEvents`.
|
||||
- `GuiRemoteMessages` provides synchronous batched request-response with auto-incrementing IDs and blocking `Submit()`.
|
||||
- `GuiRemoteController` implements `INativeController` and all sub-services as virtual stubs: single window only, intentionally null clipboard/dialog services, synchronous key state queries.
|
||||
- Connection lifecycle: `SetupRemoteNativeController` creates a layered stack (`GuiRemoteController` → `GuiHostedController` → resource managers), with connect/disconnect/reconnection handling that re-sends all window state.
|
||||
- `GuiRemoteController` implements `INativeController` and all sub-services as virtual stubs: single window only, intentionally null clipboard/dialog/automation services, synchronous key state queries.
|
||||
- Connection lifecycle: `SetupRemoteNativeController` creates a layered stack (`GuiRemoteController` → `GuiHostedController` → resource managers), with connect/disconnect/reconnection handling that re-sends all window state and explicit forced-exit handling from the renderer.
|
||||
- Rendering pipeline: element lifecycle via ID allocation, diff-based element updates, frame rendering flow (`StartRenderingOnNativeWindow` → traversal → `StopRenderingOnNativeWindow`), and measurement feedback loop (font heights, min sizes, image metadata, inline object bounds).
|
||||
- `GuiRemoteGraphicsParagraph` handles rich text with run property system, incremental diff synchronization, delegated layout queries, and caret bounds caching.
|
||||
- DOM diff layer (`GuiRemoteProtocolDomDiffConverter`) converts per-frame command streams into diffed tree structures.
|
||||
|
||||
@@ -132,8 +132,8 @@ Inter-process text transport and typed named-channel communication for applicati
|
||||
- Use `INetworkProtocolServer`, `INetworkProtocolClient`, `INetworkProtocolConnection` and `INetworkProtocolCallback` for raw asynchronous text-message transport.
|
||||
- Use `IChannelServer<TPackage>`, `IChannelClient<TPackage>`, `IChannel<TPackage>` and `IChannelReader<TPackage>` for typed named channels with client ids, direct sends, broadcasts and batched writes.
|
||||
- Use `NetworkProtocolChannelServer<TPackage, TSerialization, TServerBase>`, `NetworkProtocolChannelClient<TPackage, TSerialization>` and `NetworkProtocolLocalChannelClient<TPackage, TSerialization>` for the default channel implementation over an `INetworkProtocol*` transport.
|
||||
- Use `NamedPipeServer` / `NamedPipeClient` and `HttpServer` / `HttpClient` only when targeting Windows, because the current built-in NamedPipe and HTTP implementations are Windows-only.
|
||||
- Use `HttpClientApi` and `HttpServerApi` when implementing or maintaining the Windows HTTP transport layer directly.
|
||||
- Use `vl::inter_process::named_pipe::NamedPipeServer` / `vl::inter_process::named_pipe::NamedPipeClient` and `vl::inter_process::windows_http::HttpServer` / `vl::inter_process::windows_http::HttpClient` only when targeting Windows, because the current built-in NamedPipe and HTTP implementations are Windows-only.
|
||||
- Use `vl::inter_process::windows_http::HttpClientApi` and `vl::inter_process::windows_http::HttpServerApi` when implementing or maintaining the Windows HTTP transport layer directly.
|
||||
|
||||
[API Explanation](./KB_VlppOS_InterProcessNetworkProtocolsAndChannels.md)
|
||||
|
||||
|
||||
@@ -13,12 +13,11 @@ The control class must:
|
||||
- **Inherit from base class**: `GuiControl` (or another control) and `Description<YourControl>` for reflection
|
||||
- **Specify template type**: Use `GUI_SPECIFY_CONTROL_TEMPLATE_TYPE(TemplateName, BaseControlType)` macro
|
||||
- **Declare state variables**: Member variables to track control state
|
||||
- **Override lifecycle methods** from `GuiControl`:
|
||||
- **Implement the generated template hooks in the `.cpp` file**:
|
||||
- `BeforeControlTemplateUninstalled_()` - cleanup before template removal
|
||||
- `AfterControlTemplateInstalled_(bool initialize)` - setup after template installation
|
||||
- `OnParentLineChanged()` - handle parent hierarchy changes
|
||||
- `OnActiveAlt()` - handle ALT key activation
|
||||
- `IsTabAvailable()` - control TAB navigation availability
|
||||
- Do not redeclare these underscore functions in the class: `GUI_SPECIFY_CONTROL_TEMPLATE_TYPE` declares them and generates the actual `BeforeControlTemplateUninstalled()` and `AfterControlTemplateInstalled(bool)` overrides
|
||||
- **Override feature-specific virtual methods only when needed**, such as `OnParentLineChanged()`, `OnActiveAlt()`, or `IsTabAvailable()`
|
||||
- **Attach event handlers**: In constructor to `boundsComposition->GetEventReceiver()` for mouse/keyboard events
|
||||
- **Define public events**: Using `compositions::GuiNotifyEvent`
|
||||
- **Define properties**: With getters/setters
|
||||
@@ -32,8 +31,6 @@ class GuiButton : public GuiControl, public Description<GuiButton>
|
||||
GUI_SPECIFY_CONTROL_TEMPLATE_TYPE(ButtonTemplate, GuiControl)
|
||||
protected:
|
||||
ButtonState controlState;
|
||||
void BeforeControlTemplateUninstalled_() override;
|
||||
void AfterControlTemplateInstalled_(bool initialize) override;
|
||||
void OnMouseDown(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments);
|
||||
void UpdateControlState();
|
||||
public:
|
||||
@@ -60,8 +57,8 @@ Implement:
|
||||
- Set up events: `eventName.SetAssociatedComposition(boundsComposition)`
|
||||
- Attach handlers: `boundsComposition->GetEventReceiver()->eventName.AttachMethod(this, &GuiYourControl::Handler)`
|
||||
- **Destructor**: Cleanup (usually minimal, automatic cleanup happens)
|
||||
- **`BeforeControlTemplateUninstalled_()`**: Clear template-specific state
|
||||
- **`AfterControlTemplateInstalled_(bool initialize)`**: Sync state to template
|
||||
- **Generated hook definition `BeforeControlTemplateUninstalled_()`**: Clear template-specific state; the definition may be empty
|
||||
- **Generated hook definition `AfterControlTemplateInstalled_(bool initialize)`**: Sync state to template; the definition may be empty when no synchronization is needed
|
||||
- Call template methods: `TypedControlTemplateObject(true)->SetState(controlState)`
|
||||
- **Event handlers**: Mouse events (`leftButtonDown`, `leftButtonUp`, `mouseEnter`, `mouseLeave`), keyboard events (`keyDown`, `keyUp`)
|
||||
- **Property getters/setters**: Update state and notify template
|
||||
@@ -221,7 +218,8 @@ When creating a control that inherits from another control (e.g., `GuiSelectable
|
||||
|
||||
### Minimal Changes Approach
|
||||
|
||||
- **Parent handles lifecycle**: Override `BeforeControlTemplateUninstalled_`, etc. only if needed
|
||||
- **Define both generated hooks**: `GUI_SPECIFY_CONTROL_TEMPLATE_TYPE` declares `BeforeControlTemplateUninstalled_()` and `AfterControlTemplateInstalled_(bool)`, so provide definitions even when they are empty
|
||||
- **Parent handles the actual lifecycle overrides**: The macro-generated overrides invoke the derived hook and chain to the parent control in the required order
|
||||
- **Parent's event handlers inherited**: No need to re-implement
|
||||
- **Focus on new functionality**: Don't repeat parent's work
|
||||
|
||||
@@ -247,7 +245,7 @@ The macro system provides:
|
||||
|
||||
- `GUI_TEMPLATE_CLASS_DECL`: Generates class declaration with properties
|
||||
- `GUI_TEMPLATE_CLASS_IMPL`: Generates implementation (constructor, destructor, property accessors)
|
||||
- `GUI_SPECIFY_CONTROL_TEMPLATE_TYPE`: Links control to its template type with automatic casting
|
||||
- `GUI_SPECIFY_CONTROL_TEMPLATE_TYPE`: Links a control to its template type, declares the two underscore hook functions, generates the lifecycle overrides, and provides typed template access
|
||||
- Property macros: Generate private field, getter, setter, and change event
|
||||
|
||||
## Minimal Working Example
|
||||
@@ -263,11 +261,7 @@ class GuiMyControl : public GuiControl, public Description<GuiMyControl>
|
||||
protected:
|
||||
// State variables
|
||||
bool myState = false;
|
||||
|
||||
// Lifecycle overrides
|
||||
void BeforeControlTemplateUninstalled_() override;
|
||||
void AfterControlTemplateInstalled_(bool initialize) override;
|
||||
|
||||
|
||||
// Event handlers
|
||||
void OnMouseClick(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments);
|
||||
|
||||
|
||||
@@ -138,11 +138,11 @@ Hosts create a hierarchy of ALT contexts that can be entered/exited:
|
||||
ALT action collection from controls (`IGuiAltActionHost::CollectAltActionsFromControl`):
|
||||
|
||||
- Recursively traverses control tree starting from the specified control
|
||||
- If control has `IGuiAltActionContainer`: collects all actions from it
|
||||
- Else if control has `IGuiAltAction` and `IsAltAvailable()` and `IsAltEnabled()`: adds single action
|
||||
- **Critical behavior**: When a control has an ALT action, executes `continue` which prevents its children from being added to the traversal queue
|
||||
- If a control has `IGuiAltActionContainer`: collects all actions from it and executes `continue`
|
||||
- Else if a control has `IGuiAltAction` and both `IsAltAvailable()` and `IsAltEnabled()` return true: adds the single action and executes `continue`
|
||||
- **Critical behavior**: These `continue` paths prevent the control's children from being added to the traversal queue
|
||||
- This creates a "barrier" effect where children are hidden unless you enter a nested ALT context
|
||||
- Recursively processes all children only if the control doesn't have its own ALT action
|
||||
- A disabled or unavailable single `IGuiAltAction` does not create a barrier; collection continues into that control's children
|
||||
|
||||
### ALT Mode Lifecycle
|
||||
|
||||
@@ -188,8 +188,7 @@ Processing typed characters in ALT mode:
|
||||
- If action has `GetActivatingAltHost()`: calls `EnterAltHost` to enter nested ALT host
|
||||
- Otherwise: calls `CloseAltHost` to exit ALT mode
|
||||
- Calls `action->OnActiveAlt()` to activate the action (usually focuses the control)
|
||||
- Sets `supressAltKey` to prevent key from being processed further
|
||||
- Returns true
|
||||
- `EnterAltKey` returns true; `GuiAltActionManager::KeyDown` then stores the source key in `supressAltKey` to prevent it from being processed further
|
||||
|
||||
#### Label Filtering (`FilterTitles`)
|
||||
|
||||
@@ -206,7 +205,8 @@ Ways to exit ALT mode:
|
||||
|
||||
- Press ESCAPE: calls `LeaveAltHost` to exit current host and restore previous
|
||||
- Press BACKSPACE: calls `LeaveAltKey` to remove last character from prefix
|
||||
- Clicking or any other key: depends on whether key matches an action
|
||||
- A mouse-button down or double-click makes `GuiGraphicsHost` call `GuiAltActionManager::CloseAltHost()`
|
||||
- An unmatched keyboard key is consumed while ALT mode remains active; an invalid prefix character is removed when it leaves no visible titles
|
||||
- `CloseAltHost`: clears all state, deletes all labels, and exits all hosts in the hierarchy
|
||||
|
||||
#### Character and Key Suppression
|
||||
@@ -220,7 +220,7 @@ Input suppression while in ALT mode:
|
||||
|
||||
#### Why Nested ALT Hosts Are Needed
|
||||
|
||||
The `continue` statement in `CollectAltActionsFromControl` creates a "barrier" when a control has its own ALT action. This prevents the control's children from being collected at the parent level. Nested ALT hosts provide a mechanism to enter the control's context and collect children's ALT actions at a nested level.
|
||||
The `continue` statements in `CollectAltActionsFromControl` create a "barrier" for an `IGuiAltActionContainer`, or for a single `IGuiAltAction` that is both available and enabled. This prevents the control's children from being collected at the parent level. Nested ALT hosts provide a mechanism to enter the control's context and collect children's ALT actions at a nested level.
|
||||
|
||||
**Design Rationales for Custom `GetActivatingAltHost` Implementations:**
|
||||
|
||||
@@ -239,13 +239,13 @@ The `continue` statement in `CollectAltActionsFromControl` creates a "barrier" w
|
||||
3. **Dynamic/Temporary Content** (`GuiVirtualDataGrid`):
|
||||
- Problem: Cell editor is created on-demand, not a permanent child
|
||||
- Default collection only sees permanent children, misses temporary editor
|
||||
- Solution: When `currentEditor` exists and has ALT action, calls `SetAltComposition(currentEditor->GetTemplate())` and `SetAltControl(focusControl, true)`, returns `this`
|
||||
- Solution: When `currentEditor` has a focus control whose `IGuiAltAction` is both available and enabled, calls `SetAltComposition(currentEditor->GetTemplate())` and `SetAltControl(focusControl, true)`, then returns `this`; otherwise clears both values and delegates to `GuiVirtualListView::GetActivatingAltHost()`
|
||||
- Result: Can navigate within cell editor using ALT keys
|
||||
|
||||
4. **Scoped Navigation for Dense Control Groups** (`GuiRibbonGroup`):
|
||||
- Problem: Ribbon groups contain many buttons, exposing all at window level creates too many conflicts
|
||||
- Default would collect all buttons at same level as the group (if group didn't have its own ALT action)
|
||||
- But: `GuiRibbonGroup` has its own ALT action (from `GuiControl` base), which blocks children via `continue`
|
||||
- But: when `GuiRibbonGroup` has an available and enabled ALT action, it blocks children via `continue`
|
||||
- Solution: Returns `this` when `IsAltAvailable()` is true, creating two-level navigation
|
||||
- Result: First press ALT+[group-key] to enter group, then press ALT+[button-key] to select button
|
||||
- Benefit: Reduces conflicts and creates logical grouping
|
||||
@@ -259,7 +259,7 @@ The `continue` statement in `CollectAltActionsFromControl` creates a "barrier" w
|
||||
|
||||
The `continue` statement in `CollectAltActionsFromControl` serves as a crucial design element:
|
||||
|
||||
- **Without nested hosts**: When a control has an ALT action, the `continue` prevents children from being collected, making them unreachable
|
||||
- **Without nested hosts**: An ALT container, or an available and enabled single ALT action, prevents children from being collected at that level
|
||||
- **With nested hosts**: `GetActivatingAltHost` provides a way to "un-hide" children by entering a nested context that re-collects them
|
||||
- **For containers like `GuiRibbonGroup`**: This creates hierarchical navigation instead of flat navigation, reducing ALT key conflicts and improving scalability for interfaces with many controls
|
||||
|
||||
@@ -278,8 +278,8 @@ The `continue` statement in `CollectAltActionsFromControl` serves as a crucial d
|
||||
|
||||
`GuiGraphicsHost::Char` processes character input in this order:
|
||||
|
||||
1. First tries `GuiTabActionManager::Char`: suppresses TAB character if just navigated
|
||||
2. Then tries `GuiAltActionManager::Char`: suppresses all input while in ALT mode
|
||||
1. First tries `GuiAltActionManager::Char`: suppresses all input while in ALT mode
|
||||
2. Then tries `GuiTabActionManager::Char`: suppresses TAB character if just navigated
|
||||
3. Finally delivers to focused composition's event receiver if not suppressed
|
||||
|
||||
### Control Visibility and Enable State
|
||||
|
||||
@@ -47,7 +47,7 @@ For remote mode in `SetupRemoteNativeController`:
|
||||
- `INativeAsyncService` — to redirect async operations through the real native controller
|
||||
- `INativeScreenService` / `INativeScreen` — to provide a virtual single screen
|
||||
- `INativeWindowService` — to create, destroy, and manage virtual windows
|
||||
- `IGuiHostedApplication` — to expose the native window host
|
||||
- `IGuiHostedApplication` — to expose both the native window host and the wrapped native controller
|
||||
|
||||
### Service Delegation
|
||||
|
||||
@@ -56,7 +56,11 @@ The controller delegates most services to the underlying native controller:
|
||||
- `ClipboardService()` → `nativeController->ClipboardService()`
|
||||
- `ImageService()` → `nativeController->ImageService()`
|
||||
- `InputService()` → `nativeController->InputService()`
|
||||
- `DialogService()` → returns `nullptr` (replaced by `FakeDialogServiceBase`)
|
||||
- `GetExecutablePath()` → `nativeController->GetExecutablePath()`
|
||||
|
||||
Services intentionally unavailable in hosted mode:
|
||||
- `DialogService()` → returns `nullptr` so GacUI uses `FakeDialogServiceBase`
|
||||
- `AutomationService()` → returns `nullptr` so GacUI uses `INativeAutomationService::UnavailableService`
|
||||
|
||||
Services it implements itself:
|
||||
- `CallbackService()` → local `SharedCallbackService` instance
|
||||
@@ -236,7 +240,7 @@ Individual mouse event methods (`LeftButtonDown`, `MouseMoving`, etc.) are wired
|
||||
|
||||
### Keyboard Events
|
||||
|
||||
`HandleKeyboardCallback` dispatches keyboard events (`KeyDown`, `KeyUp`, `Char`) to the active window's listeners.
|
||||
`HandleKeyboardCallback` dispatches keyboard events (`KeyDown`, `KeyUp`, `Char`) to the active window's listeners. If there is no active window, no window-manager operation is in progress, and the main window exists, it activates the main window before dispatching.
|
||||
|
||||
## Rendering Pipeline
|
||||
|
||||
@@ -245,8 +249,9 @@ Individual mouse event methods (`LeftButtonDown`, `MouseMoving`, etc.) are wired
|
||||
`GuiHostedController::GlobalTimer()` drives the rendering cycle. On each global timer tick:
|
||||
1. Skip if the native window is not visible or already in hosted rendering
|
||||
2. Check all visible windows' listeners for `NeedRefresh()` — if any returns true, set `needRefresh`
|
||||
3. If no refresh is needed and nothing was updated last frame, skip rendering
|
||||
3. If no refresh is needed and nothing was updated last frame, call `renderTarget->HostedRenderingIdle()` once for this transition to idle, then skip rendering
|
||||
4. Enter rendering loop:
|
||||
- Reset the idle-notified state because rendering is active again
|
||||
- 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)`
|
||||
@@ -260,6 +265,7 @@ Individual mouse event methods (`LeftButtonDown`, `MouseMoving`, etc.) are wired
|
||||
- `StartHostedRendering()` sets `hostedRendering = true` and calls `StartRenderingOnNativeWindow()` once
|
||||
- During hosted rendering, individual `StartRendering()` / `StopRendering()` pairs do NOT call `StartRenderingOnNativeWindow()` / `StopRenderingOnNativeWindow()` — they just toggle the `rendering` flag
|
||||
- `StopHostedRendering()` calls `StopRenderingOnNativeWindow()` once
|
||||
- `HostedRenderingIdle()` is a hook indicating that the hosted controller expects no more rendering until state changes; the base implementation is empty, while `GuiRemoteGraphicsRenderTarget` sends `RequestRendererIdle()`
|
||||
- This means all hosted windows render within a single begin/end rendering session on the native render target
|
||||
|
||||
### Per-Window Rendering Offset (GuiGraphicsHost)
|
||||
|
||||
@@ -22,7 +22,7 @@ This document explains the end-to-end design and required implementation steps f
|
||||
|
||||
4. IGuiGraphicsRenderTarget (and concrete subclasses per backend)
|
||||
- Encapsulates drawing surface, clip stack (`PushClipper`, `PopClipper`, `GetClipper`, `IsClipperCoverWholeTarget`).
|
||||
- Manages rendering phases (`StartRendering`, `StopRendering`, hosted variants) and reports device failures (`RenderTargetFailure`).
|
||||
- Manages rendering phases (`StartRendering`, `StopRendering`, `StartHostedRendering`, `StopHostedRendering`), receives hosted-idle notification through `HostedRenderingIdle()`, and reports device failures (`RenderTargetFailure`).
|
||||
|
||||
|
||||
5. GuiGraphicsRenderTarget (base implementation)
|
||||
@@ -39,6 +39,7 @@ A lightweight element (e.g., `GuiSolidLabelElement`) follows this template patte
|
||||
- No rendering code appears in the element itself; it only stores state.
|
||||
|
||||
Typical additional element examples in `GuiGraphicsElement.h / .cpp` (all using the same pattern):
|
||||
- `GuiFocusRectangleElement`,
|
||||
- `GuiSolidBorderElement`, `Gui3DBorderElement`, `Gui3DSplitterElement` (two-color, directional),
|
||||
- `GuiSolidBackgroundElement`, `GuiGradientBackgroundElement`,
|
||||
- `GuiInnerShadowElement`,
|
||||
@@ -72,24 +73,24 @@ Each concrete renderer is registered exactly once through its static `Register()
|
||||
## 5. Registration Flow
|
||||
|
||||
1. Application selects backend (e.g., Direct2D or GDI), calling `RendererMainDirect2D()` or `RendererMainGDI()`.
|
||||
2. Inside these functions each renderer’s `Register()` is invoked (e.g., `GuiSolidLabelElementRenderer::Register()`).
|
||||
3. `Register()` calls `GetGuiGraphicsResourceManager()->RegisterRendererFactory(ElementTypeName, factory)` linking element type to a factory.
|
||||
2. Inside these functions each renderer's `Register()` is invoked (e.g., `GuiSolidLabelElementRenderer::Register()`).
|
||||
3. `Register()` obtains the integer element type from `TElement::GetElementType()` and calls `GetGuiGraphicsResourceManager()->RegisterRendererFactory(elementType, factory)` to link it to a factory.
|
||||
4. When an element instance is created via `GuiElementBase<T>::Create()`, the resource manager looks up the factory and constructs a matching renderer, calls `Initialize(element)`.
|
||||
5. When a composition later receives a render target, it propagates to bound elements’ renderers through `SetRenderTarget` (under control of composition tree traversal / host initialization).
|
||||
5. When a composition later receives a render target, it propagates to bound elements' renderers through `SetRenderTarget` (under control of composition tree traversal / host initialization).
|
||||
|
||||
## 6. Composition Ownership and Rendering Invocation
|
||||
|
||||
- A `GuiGraphicsComposition` holds at most one `IGuiGraphicsElement`. On attach it calls the element’s protected `SetOwnerComposition`.
|
||||
- A `GuiGraphicsComposition` holds at most one `IGuiGraphicsElement`. On attach it calls the element's protected `SetOwnerComposition`.
|
||||
- Rendering pipeline (`GuiGraphicsHost::Render()`):
|
||||
1. Host invokes `windowComposition->Render(offset)` recursively.
|
||||
2. Each composition pushes clipping, then (if it has an element) obtains `element->GetRenderer()->Render(bounds)`.
|
||||
3. Render target clipping stack managed by `GuiGraphicsRenderTarget::PushClipper` / `PopClipper` ensures nested composition visibility.
|
||||
2. Each visible composition first calls `element->GetRenderer()->Render(bounds)` for its own element when one exists.
|
||||
3. When child traversal or interaction metadata requires a boundary, the composition pushes its bounds clipper, renders its children recursively, and then pops the clipper. The `GuiGraphicsRenderTarget::PushClipper` / `PopClipper` stack ensures nested composition visibility.
|
||||
4. After traversal `StopRendering()` finalizes; any `RenderTargetFailure` is processed (e.g., lost device or resize triggers re-creation of render target and re-run of render).
|
||||
|
||||
## 7. State Change Propagation and Invalidation Chain
|
||||
|
||||
1. Setter in element detects a change and calls `InvokeOnElementStateChanged()` (provided by `GuiElementBase<T>`).
|
||||
2. That method calls the bound renderer’s `OnElementStateChanged()` so it can drop caches (brushes, layouts) or lazily refresh on next `Render`.
|
||||
2. That method calls the bound renderer's `OnElementStateChanged()` so it can drop caches (brushes, layouts) or lazily refresh on next `Render`.
|
||||
3. `InvokeOnElementStateChanged()` also raises composition invalidation causing `GuiGraphicsHost` to mark `needRender = true`.
|
||||
4. The main loop / timer triggers `GuiGraphicsHost::GlobalTimer()`, which if `needRender` calls `Render()`.
|
||||
5. Min size recalculation done inside renderer (e.g., `GuiSolidLabelElementRenderer::UpdateMinSize()`) influences subsequent layout passes.
|
||||
@@ -103,7 +104,7 @@ Each concrete renderer is registered exactly once through its static `Register()
|
||||
## 9. Resource Lifetime and Render Target Changes
|
||||
|
||||
- Persistent resources independent of target (e.g., cached last element values) retained across target switches.
|
||||
- Target-bound resources (Direct2D brushes, text layouts, GDI pens/brushes, bitmaps) created in `InitializeInternal()` if target already set, or in `RenderTargetChangedInternal()` when a new target arrives.
|
||||
- A renderer factory initializes `renderTarget` to null before `InitializeInternal()` runs. Target-independent state can be created in `InitializeInternal()`; target-bound resources are created in `RenderTargetChangedInternal()` when a target is assigned.
|
||||
- On target loss (device lost / resize reported via `RenderTargetFailure`), host re-acquires target; each renderer releases old target objects in `RenderTargetChangedInternal(old, nullptr)` then recreates after new target available.
|
||||
|
||||
## 10. Adding a New Lightweight Element (Checklist)
|
||||
@@ -122,6 +123,9 @@ Renderer (per backend):
|
||||
Registration:
|
||||
- Insert `GuiXxxElementRenderer::Register()` in each backend initialization (Direct2D: `RendererMainDirect2D`, GDI: `RendererMainGDI`, Remote: remote resource manager initialization).
|
||||
|
||||
Reflection and XML:
|
||||
- Add the element to the reflection type list and register its constructor, properties, methods, and any supporting enum or struct types. XML `<Xxx>` construction depends on this reflection registration.
|
||||
|
||||
Testing:
|
||||
- Instantiate via XML `<Xxx>` mapping to `presentation::elements::GuiXxxElement` or directly create in C++ via `GuiXxxElement::Create()`.
|
||||
- Verify property mutations trigger re-render (breakpoint or visual change) and min size recomputation.
|
||||
@@ -139,7 +143,7 @@ Testing:
|
||||
## 12. Common Pitfalls
|
||||
|
||||
- Forgetting to compare old vs new value in setter: causes redundant invalidations and potential performance issues.
|
||||
- Allocating target-bound resources in constructor instead of `InitializeInternal()` / `RenderTargetChangedInternal()` leads to null target usage or leaks.
|
||||
- Allocating target-bound resources in the constructor or `InitializeInternal()` instead of `RenderTargetChangedInternal()` leads to null target usage or leaks.
|
||||
- Not releasing resources in `FinalizeInternal()` or when `RenderTargetChangedInternal(new == nullptr)` => leaks on device reset.
|
||||
- Failing to update min size after relevant property change (text/font/wrap) => layout flickers or stale size.
|
||||
- Omitting `Register()` call => element silently renders nothing (renderer never created).
|
||||
@@ -174,6 +178,7 @@ Shutdown: Renderer `Finalize()` (invokes `FinalizeInternal()`), composition rele
|
||||
- [ ] `Render` draws respecting bounds & element properties.
|
||||
- [ ] `OnElementStateChanged` invalidates & triggers min size recalculation.
|
||||
- [ ] Registration calls inserted in each backend entry point.
|
||||
- [ ] Reflection type-list and member registrations added for XML construction.
|
||||
- [ ] Remote protocol enums / serialization (if remote supported) implemented.
|
||||
- [ ] Manual / unit tests cover property changes & rendering.
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ Responsibilities:
|
||||
* `Layout_UpdateMinSize()` (calls overridden min size function then caches and fires `CachedMinSizeChanged`).
|
||||
* `Layout_UpdateBounds(Size parentSize)` (computes own bounds then recurses to children).
|
||||
* `Layout_CalculateMinSizeHelper()` shared policy used by most containers.
|
||||
- Invalidation trigger: `InvokeOnCompositionStateChanged(forceRequestRender)` sets flags; does not directly perform layout.
|
||||
- Invalidation trigger: `InvokeOnCompositionStateChanged(forceRequestRender)` calls the virtual `OnCompositionStateChanged()` hook and requests rendering when the composition has a related host and is visible (or rendering is forced). Container overrides use the hook to set their own dirty flags; the base method does not perform layout.
|
||||
- Synchronous optional recompute: `ForceCalculateSizeImmediately()` (used sparingly, e.g., interactive splitters).
|
||||
- Rendering entry: `Render` consumes only `cachedBounds` (no layout recalculation inside rendering).
|
||||
|
||||
@@ -28,13 +28,13 @@ Responsibilities:
|
||||
Purpose: Distinguish measurement & arrangement control patterns.
|
||||
1. `GuiGraphicsComposition_Trivial`
|
||||
- Container autonomously computes measurement and bounds using standard algorithm (often via `Layout_CalculateMinSizeHelper`).
|
||||
- Examples: `GuiBoundsComposition`, `GuiTableComposition`, `GuiStackComposition`, `GuiFlowComposition`, `GuiSharedSizeRootComposition` (directly or indirectly).
|
||||
- Examples: `GuiBoundsComposition`, `GuiTableComposition`, `GuiStackComposition`, `GuiFlowComposition`, `GuiSharedSizeRootComposition`, and `GuiSharedSizeItemComposition` (directly or indirectly).
|
||||
2. `GuiGraphicsComposition_Controlled`
|
||||
- Measurement / bounds set externally by parent; overrides `Layout_CalculateMinSize` & `Layout_CalculateBounds` to simply return cached values (no internal recompute). Parent writes caches using specialized setters (e.g., `GuiCellComposition::Layout_SetCellBounds`, `GuiStackItemComposition::Layout_SetStackItemBounds`, `GuiFlowItemComposition::Layout_SetFlowItemBounds`).
|
||||
- Typical for helper items: `GuiCellComposition`, splitters, stack items, flow items, shared size items.
|
||||
- Used by `GuiCellComposition`, `GuiStackItemComposition`, and `GuiFlowItemComposition`.
|
||||
3. `GuiGraphicsComposition_Specialized`
|
||||
- Container with custom arrangement while limiting child -> parent enlargement; usually still uses `Layout_CalculateMinSizeHelper` but overrides `Layout_CalculateMinClientSizeForParent` to `{0,0}`.
|
||||
- Examples: `GuiSideAlignedComposition`, `GuiPartialViewComposition`, `GuiWindowComposition`.
|
||||
- Examples: `GuiSideAlignedComposition`, `GuiPartialViewComposition`, `GuiWindowComposition`, and `GuiTableSplitterCompositionBase` (the base of row and column splitters).
|
||||
|
||||
## 3. Layout Pass Orchestration
|
||||
### 3.1 Measurement Flow
|
||||
@@ -55,9 +55,9 @@ Purpose: Distinguish measurement & arrangement control patterns.
|
||||
3. Controlled children rely on parent precomputed rectangles delivered via container-specific setters instead of independent calculation.
|
||||
|
||||
### 3.3 Host-Driven Iteration
|
||||
- Changes call `InvokeOnCompositionStateChanged()` marking layout invalid (and optionally requesting a render).
|
||||
- `GuiGraphicsHost::Render` loops: if any layout-invalid flag is set for the window root subtree, perform a full layout (measurement + arrangement cascade), then render. Repeats until no invalidation remains.
|
||||
- Ensures deterministic stabilization without requiring synchronous recalculation inside property setters.
|
||||
- Changes call `InvokeOnCompositionStateChanged()`. Composition-specific hooks can mark cached calculations dirty, while the base method requests a render from the related host.
|
||||
- A `GuiGraphicsHost::Render` call first renders the composition tree using its current `cachedBounds`. After successful rendering, it performs the window-root measurement and arrangement cascade.
|
||||
- If updating cached sizes or bounds requests another render, a later `GlobalTimer` / `Render` cycle consumes the new layout. Cascading invalidations therefore stabilize across successive render cycles, not through an inner loop in one `Render` call.
|
||||
- `ForceCalculateSizeImmediately()` is an optimization for immediate feedback inside event handlers (e.g., splitter dragging) but never required for correctness.
|
||||
|
||||
## 4. Invalidation Sources & Flags
|
||||
@@ -84,7 +84,7 @@ Purpose: Distinguish measurement & arrangement control patterns.
|
||||
|
||||
### 5.2 Child -> Parent Constraint
|
||||
- During parent measurement, after calling each child's `Layout_UpdateMinSize`, parent optionally adds `child->Layout_CalculateMinClientSizeForParent(internalMargin)` to its accumulated client size if own `minSizeLimitation == LimitToElementAndChildren`.
|
||||
- Controlled children often contribute `{0,0}` because parent already manually accounts for them (e.g., table computing row/column sizes; stack computing cumulative lengths; flow computing wrapping layout).
|
||||
- Controlled children contribute `{0,0}` because their parent manually accounts for them (e.g., table computing row/column sizes; stack computing cumulative lengths; flow computing wrapping layout).
|
||||
|
||||
## 6. Predefined Layout Families
|
||||
Distinct algorithms (excluding helper-only controlled items):
|
||||
@@ -96,7 +96,9 @@ Distinct algorithms (excluding helper-only controlled items):
|
||||
6. Side Aligned: `GuiSideAlignedComposition` (dock to a specified side with length ratio / max constraints).
|
||||
7. Partial View: `GuiPartialViewComposition` (scroll / viewport style fractional sub-rectangle selection with offsets).
|
||||
8. Window Root: `GuiWindowComposition` (ties client area to native window size, root for layout passes).
|
||||
Helper controlled participants: `GuiCellComposition`, `GuiStackItemComposition`, `GuiFlowItemComposition`, `GuiSharedSizeItemComposition`, `GuiRowSplitterComposition`, `GuiColumnSplitterComposition`.
|
||||
9. Repeat: `GuiRepeatStackComposition` and `GuiRepeatFlowComposition` for non-virtual repeated templates, plus `GuiVirtualRepeatCompositionBase` and its free-height, fixed-height, fixed-size multi-column, and fixed-height multi-column implementations for virtualized item layout.
|
||||
|
||||
Controlled helper participants are `GuiCellComposition`, `GuiStackItemComposition`, and `GuiFlowItemComposition`. `GuiSharedSizeItemComposition` is a bounds/trivial composition, while `GuiRowSplitterComposition` and `GuiColumnSplitterComposition` derive through the specialized `GuiTableSplitterCompositionBase`.
|
||||
|
||||
## 7. Internal Optimization Flags & Lazy Strategies
|
||||
- Table maintains `layout_invalid`, `layout_invalidCellBounds`, recalculating row/column metrics and cell rectangles only when flagged inside `Layout_CalculateMinSize` / `Layout_CalculateBounds` (helpers: `Layout_UpdateCellBoundsInternal`, `Layout_UpdateCellBoundsPercentages`, `Layout_UpdateCellBoundsOffsets`).
|
||||
@@ -156,9 +158,9 @@ Add adaptive multi-level representations (compact to expanded) without duplicati
|
||||
## 9. Detailed Constraint Examples
|
||||
### 9.1 Table Example (Column Percentage Change)
|
||||
1. Setter `GuiTableComposition::SetColumnOption` updates model then calls `InvokeOnCompositionStateChanged()`.
|
||||
2. Next `GuiGraphicsHost::Render` detects invalid layout, triggers full window layout.
|
||||
3. Table `Layout_CalculateMinSize` sees `layout_invalid` -> recomputes metrics; `Layout_CalculateBounds` updates each `GuiCellComposition` via `Layout_SetCellBounds`.
|
||||
4. Rendering consumes new `cachedBounds`.
|
||||
2. The next `GuiGraphicsHost::Render` renders the current cached layout, then runs the full window measurement and arrangement cascade.
|
||||
3. Table `Layout_CalculateMinSize` sees `layout_invalid` and recomputes metrics; `Layout_CalculateBounds` updates each `GuiCellComposition` via `Layout_SetCellBounds`.
|
||||
4. Changed cached bounds request another render, and a subsequent cycle renders the new geometry.
|
||||
|
||||
### 9.2 Interactive Splitter Drag
|
||||
1. Mouse move updates adjacent Absolute options.
|
||||
@@ -189,11 +191,11 @@ Steps:
|
||||
|
||||
## 12. Separation of Concerns
|
||||
- Layout computation (measurement + arrangement) is strictly segregated from rendering (`Render` side-effect-free wrt layout state).
|
||||
- Invalidation is single entry (`InvokeOnCompositionStateChanged`) leaving scheduling & iteration to the host.
|
||||
- Invalidation enters through `InvokeOnCompositionStateChanged`, leaving scheduling to the host and container-specific dirty state to `OnCompositionStateChanged` overrides.
|
||||
- Responsive system layers on top without altering the base pass algorithm; it only manipulates subtree structure and min-size outputs between passes.
|
||||
|
||||
## 13. Reliability & Stability Patterns
|
||||
- Iterative host loop ensures eventual fixpoint when cascading invalidations occur (e.g., responsive level changes causing new size measurements).
|
||||
- Successive host render cycles converge when cascading invalidations occur (e.g., responsive level changes causing new size measurements).
|
||||
- Dirty flag + lazy recomputation prevents redundant heavy recomputations (especially table & flow geometry).
|
||||
- Controlled compositions decouple per-item geometry from measuring logic to simplify parent algorithms.
|
||||
|
||||
@@ -205,6 +207,7 @@ Steps:
|
||||
- Stack: `GuiStackComposition::{SetDirection,Layout_UpdateStackItemMinSizes,Layout_CalculateMinSize,Layout_UpdateStackItemBounds}`, item setter `GuiStackItemComposition::Layout_SetStackItemBounds`.
|
||||
- Flow: `GuiFlowComposition::{SetAxis,SetRowPadding,Layout_UpdateFlowItemLayout,Layout_UpdateFlowItemLayoutByConstraint}`, item setter `GuiFlowItemComposition::Layout_SetFlowItemBounds`.
|
||||
- Shared Size: `GuiSharedSizeRootComposition::{CalculateOriginalMinSizes,CollectSizes,AlignSizes}`, item logic `GuiSharedSizeItemComposition::Layout_CalculateMinSize`.
|
||||
- Repeat: `GuiRepeatCompositionBase`, `GuiNonVirtialRepeatCompositionBase`, `GuiRepeatStackComposition`, `GuiRepeatFlowComposition`, `GuiVirtualRepeatCompositionBase`, and the `GuiRepeat*ItemComposition` implementations.
|
||||
- Splitters: `GuiTableSplitterCompositionBase::OnMouseMoveHelper`.
|
||||
- Responsive Base: `GuiResponsiveCompositionBase::{OnParentLineChanged,OnResponsiveChildLevelUpdated}`.
|
||||
- Responsive View: `GuiResponsiveViewComposition::{LevelDown,LevelUp,CalculateLevelCount,CalculateCurrentLevel}`.
|
||||
@@ -215,4 +218,4 @@ Steps:
|
||||
- Responsive Container: `GuiResponsiveContainerComposition::{Layout_CalculateBounds,Layout_AdjustLevelUp,Layout_AdjustLevelDown,Layout_CompareSize}`.
|
||||
|
||||
## 15. Summary
|
||||
The GacUI layout engine employs a clean separation between invalidation, measurement, arrangement, and rendering, with composition classification enabling optimized parent-child cooperation patterns. Predefined container families cover common layout paradigms (grid, linear, wrapping, docking, viewport, shared sizing) while the responsive layer introduces adaptive multi-level UI transformation without complicating the core pass. Extension points are well-localized: new algorithms implement three virtual layout functions (or override contribution behavior), while responsive strategies implement discrete level semantics. Lazy flags and controlled item abstractions keep recomputation efficient, and host-driven iterative stabilization guarantees correctness even under cascading dynamic changes.
|
||||
The GacUI layout engine employs a clean separation between invalidation, measurement, arrangement, and rendering, with composition classification enabling optimized parent-child cooperation patterns. Predefined container families cover common layout paradigms (grid, linear, wrapping, docking, viewport, shared sizing, and repeated/virtualized items) while the responsive layer introduces adaptive multi-level UI transformation without complicating the core pass. Extension points are well-localized: new algorithms implement three virtual layout functions (or override contribution behavior), while responsive strategies implement discrete level semantics. Lazy flags and controlled item abstractions keep recomputation efficient, and successive host render cycles guarantee eventual stabilization even under cascading dynamic changes.
|
||||
|
||||
@@ -72,7 +72,7 @@ Modal windows in GacUI provide traditional blocking semantics without actually b
|
||||
- Ensures proper resource cleanup after modal completion
|
||||
|
||||
**ShowModalAsync(owner):**
|
||||
- Returns `IFuture` for async/await pattern integration
|
||||
- Returns `Ptr<reflection::description::IAsync>` for async/await integration; the implementation creates an `IFuture` internally and returns it through the async interface
|
||||
- Demonstrates the truly non-blocking nature of the modal system
|
||||
- Enables modern asynchronous programming patterns
|
||||
|
||||
@@ -132,4 +132,4 @@ The architecture successfully abstracts platform differences while providing ric
|
||||
|
||||
**Platform-Optimized Implementation**: Each platform uses its optimal event processing mechanism while maintaining the same high-level behavior.
|
||||
|
||||
This design enables developers to create sophisticated GUI applications with complex modal dialog patterns that work consistently across Windows native, hosted, and remote environments while maintaining the responsiveness expected in modern applications.
|
||||
This design enables developers to create sophisticated GUI applications with complex modal dialog patterns that work consistently across Windows native, hosted, and remote environments while maintaining the responsiveness expected in modern applications.
|
||||
|
||||
@@ -7,20 +7,22 @@ GacUI is designed to support multiple platforms with different rendering backend
|
||||
1. **Windows Direct2D** - Modern Windows graphics using Direct2D and DirectWrite with hardware acceleration
|
||||
2. **Windows GDI** - Legacy Windows graphics using traditional GDI for compatibility with older systems
|
||||
3. **Linux GTK** - Linux platform support with GTK rendering (declared as `SetupGtkRenderer()` but implementation in separate repository)
|
||||
4. **macOS Cocoa** - macOS platform support with Core Graphics rendering (declared as `SetupOSXCoreGraphicsRenderer()` but implementation in separate repository)
|
||||
5. **Remote Rendering** - Platform-agnostic remote rendering over network protocols for testing and distributed applications
|
||||
6. **Code Generation** - Special mode for compile-time code generation (GacGen)
|
||||
4. **Wayland WGac** - Wayland support with standard and hosted entry points (`elements::wgac::SetupWGacRenderer()` and `elements::wgac::SetupWGacHostedRenderer()`)
|
||||
5. **macOS Cocoa** - macOS platform support with Core Graphics rendering (declared as `SetupOSXCoreGraphicsRenderer()` and `SetupOSXHostedCoreGraphicsRenderer()` but implemented in a separate repository)
|
||||
6. **Remote Rendering** - Platform-agnostic remote rendering over network protocols for testing and distributed applications
|
||||
7. **Code Generation** - Special mode for compile-time code generation through `SetupGacGenNativeController()`
|
||||
|
||||
The actual Linux and macOS implementations are maintained in separate repositories, but the entry points are declared in this codebase to maintain API consistency. The architecture is designed for extensibility, with clear separation between platform-specific implementations and the core framework.
|
||||
The GTK, WGac, and macOS entry points are declared in this codebase to maintain API consistency, while their implementations are supplied separately. The architecture is designed for extensibility, with clear separation between platform-specific implementations and the core framework.
|
||||
|
||||
## Entry Point Architecture
|
||||
|
||||
The initialization system uses a consistent naming pattern for entry points: `Setup[Platform][Renderer][Mode]()`. Each combination provides different capabilities:
|
||||
Entry-point names are platform-specific. Windows puts `Hosted` or `Raw` before the platform and renderer name, while WGac and macOS use hosted suffixes, and remote and code-generation modes use `NativeController` names.
|
||||
|
||||
### Standard Mode Entry Points
|
||||
- `SetupWindowsDirect2DRenderer()` - Full Direct2D application with complete framework and native OS windows
|
||||
- `SetupWindowsGDIRenderer()` - Full GDI application with complete framework and native OS windows
|
||||
- `SetupGtkRenderer()` - Full Linux/GTK application (implementation in separate repository)
|
||||
- `elements::wgac::SetupWGacRenderer()` - Full Wayland/WGac application (implementation supplied separately)
|
||||
- `SetupOSXCoreGraphicsRenderer()` - Full macOS application (implementation in separate repository)
|
||||
- `SetupRemoteNativeController(protocol)` - Full remote application with protocol communication
|
||||
|
||||
@@ -29,6 +31,8 @@ Standard mode provides the complete GacUI application framework including `GuiAp
|
||||
### Hosted Mode Entry Points
|
||||
- `SetupHostedWindowsDirect2DRenderer()` - Direct2D embedded within a single native OS window
|
||||
- `SetupHostedWindowsGDIRenderer()` - GDI embedded within a single native OS window
|
||||
- `elements::wgac::SetupWGacHostedRenderer()` - Hosted Wayland/WGac application (implementation supplied separately)
|
||||
- `SetupOSXHostedCoreGraphicsRenderer()` - Hosted macOS application (implementation supplied separately)
|
||||
|
||||
Hosted mode runs the entire GacUI application within only one native OS window. All GacUI sub-windows, dialogs, and menus are rendered as graphics rather than creating additional native OS windows. This is achieved by wrapping the native controller with `GuiHostedController`, which provides window abstraction while sharing the host application's window handle.
|
||||
|
||||
@@ -38,6 +42,9 @@ Hosted mode runs the entire GacUI application within only one native OS window.
|
||||
|
||||
Raw mode provides minimal rendering capabilities without the application framework. It completely bypasses `GuiApplication` and `GuiWindow` creation, calling `GuiRawMain()` instead of the full application initialization. This mode is suitable for custom applications that need direct control over initialization and only require graphics rendering capabilities.
|
||||
|
||||
### Special-Purpose Entry Point
|
||||
- `SetupGacGenNativeController()` - Runs the application framework with the code-generation native controller.
|
||||
|
||||
## Initialization Flow
|
||||
|
||||
The initialization process follows a consistent six-phase sequence from platform entry point to user code:
|
||||
@@ -47,9 +54,9 @@ The process begins at platform-specific entry points (`WinMain` on Windows, `mai
|
||||
|
||||
### Phase 2: Setup Function Routing
|
||||
Setup functions route to internal implementation functions:
|
||||
- `SetupWindowsDirect2DRenderer()` ? `SetupWindowsDirect2DRendererInternal(false, false)`
|
||||
- `SetupHostedWindowsDirect2DRenderer()` ? `SetupWindowsDirect2DRendererInternal(true, false)`
|
||||
- `SetupRawWindowsDirect2DRenderer()` ? `SetupWindowsDirect2DRendererInternal(false, true)`
|
||||
- `SetupWindowsDirect2DRenderer()` → `SetupWindowsDirect2DRendererInternal(false, false)`
|
||||
- `SetupHostedWindowsDirect2DRenderer()` → `SetupWindowsDirect2DRendererInternal(true, false)`
|
||||
- `SetupRawWindowsDirect2DRenderer()` → `SetupWindowsDirect2DRendererInternal(false, true)`
|
||||
|
||||
### Phase 3: Renderer Main Functions
|
||||
Internal setup functions call platform-specific renderer main functions:
|
||||
@@ -58,8 +65,8 @@ Internal setup functions call platform-specific renderer main functions:
|
||||
|
||||
### Phase 4: Application vs Raw Initialization
|
||||
Renderer main functions branch based on mode:
|
||||
- **Standard/Hosted Mode**: `GuiApplicationMain()` ? `GuiApplicationInitialize()`
|
||||
- **Raw Mode**: `GuiRawInitialize()` ? `GuiRawMain()`
|
||||
- **Standard/Hosted Mode**: `GuiApplicationMain()` → `GuiApplicationInitialize()` → user-defined `GuiMain()`
|
||||
- **Raw Mode**: `GuiRawMain()` → `GuiRawInitialize()` → user-defined `GuiMain()`
|
||||
|
||||
### Phase 5: Framework Setup (Non-Raw Only)
|
||||
`GuiApplicationInitialize()` performs comprehensive framework initialization:
|
||||
@@ -154,9 +161,9 @@ static void Register()
|
||||
|
||||
The registration mechanism uses the `GuiElementRendererBase` template which provides the `Register()` static method that calls `GetGuiGraphicsResourceManager()->RegisterRendererFactory()` to bind element types to renderer factories.
|
||||
|
||||
## Service Registration and Dependencies
|
||||
## Native Controller Services
|
||||
|
||||
The application framework registers services in a specific order to handle dependencies correctly:
|
||||
`INativeController` exposes the following services to the application framework:
|
||||
|
||||
1. **Callback Service** - Foundation event dispatch system
|
||||
2. **Resource Service** - System fonts, cursors, and default resources
|
||||
@@ -167,6 +174,7 @@ The application framework registers services in a specific order to handle depen
|
||||
7. **Window Service** - Window creation, management, and lifecycle
|
||||
8. **Input Service** - Keyboard, mouse, and timer services
|
||||
9. **Dialog Service** - File dialogs and message boxes
|
||||
10. **Automation Service** - Optional automation access for inspecting and controlling an application
|
||||
|
||||
## Remote Mode Architecture
|
||||
|
||||
@@ -220,4 +228,4 @@ Proper cleanup through systematic resource management:
|
||||
- Thread-local storage proper disposal
|
||||
- Symmetric plugin loading and unloading
|
||||
|
||||
This comprehensive initialization system enables GacUI to provide consistent, high-performance cross-platform GUI capabilities while maintaining platform-specific optimizations and providing extensive testing and debugging support.
|
||||
This comprehensive initialization system enables GacUI to provide consistent, high-performance cross-platform GUI capabilities while maintaining platform-specific optimizations and providing extensive testing and debugging support.
|
||||
|
||||
@@ -21,7 +21,7 @@ Messages are requests sent from the core side to the renderer side. They cover c
|
||||
|
||||
### IGuiRemoteProtocolEvents (Renderer Side → Core Side)
|
||||
|
||||
Events are notifications sent from the renderer side to the core side. They include connection lifecycle (`OnControllerConnect`, `OnControllerDisconnect`, `OnControllerRequestExit`), user input (`OnIOKeyDown`, `OnIOKeyUp`, `OnIOMouseMoving`, etc.), and window state changes (`OnWindowBoundsUpdated`).
|
||||
Events are notifications sent from the renderer side to the core side. They include connection lifecycle (`OnControllerConnect`, `OnControllerDisconnect`, `OnControllerRequestExit`, `OnControllerForceExit`), user input (`OnIOKeyDown`, `OnIOKeyUp`, `OnIOMouseMoving`, etc.), and window state changes (`OnWindowBoundsUpdated`).
|
||||
|
||||
Responses are also delivered through `IGuiRemoteProtocolEvents` via `RespondNAME(id, arguments)` methods, matched to their originating request by request ID.
|
||||
|
||||
@@ -54,7 +54,7 @@ The core side operates synchronously from its own perspective despite the underl
|
||||
### Service Design Decisions
|
||||
|
||||
- **Single window only**: `CreateNativeWindow` can only be called once (enforced by `CHECK_ERROR`). Multiple sub-windows are managed by `GuiHostedController` through hosted mode.
|
||||
- **Intentionally null services**: `ClipboardService()` and `DialogService()` return `nullptr`, causing GacUI to fall back to built-in fakes (`FakeDialogServiceBase`). This is intentional — the core side cannot access real OS clipboard or dialogs.
|
||||
- **Intentionally null services**: `ClipboardService()`, `DialogService()`, and `AutomationService()` return `nullptr`. GacUI consequently uses its fake clipboard, `FakeDialogServiceBase`, and `INativeAutomationService::UnavailableService`; the core side cannot directly use those renderer-side OS services.
|
||||
- **Synchronous key state queries**: `IsKeyPressing()` and `IsKeyToggled()` perform synchronous request-submit-retrieve round-trips. This is expensive but rarely called.
|
||||
- **Global shortcut keys**: Tracked core-side and batch-sent to the renderer side on update.
|
||||
|
||||
@@ -85,14 +85,14 @@ Channel servers distinguish the local core client from renderer clients through
|
||||
|
||||
The renderer side fires `OnControllerDisconnect()`. Each subsystem (`GuiRemoteWindow`, `GuiRemoteGraphicsImageService`, `GuiRemoteGraphicsResourceManager`) marks itself disconnected and suspends protocol communication until reconnection.
|
||||
|
||||
### Graceful Exit
|
||||
### Graceful and Forced Exit
|
||||
|
||||
The renderer side fires `OnControllerRequestExit()`:
|
||||
1. `remoteWindow.Hide(true)` triggers the close sequence
|
||||
2. `BeforeClosing` / `AfterClosing` listener chain fires
|
||||
3. `DestroyNativeWindow` is scheduled
|
||||
|
||||
`connectionForcedToStop` bypasses the `BeforeClosing` cancellation check for forced shutdown.
|
||||
For a forced shutdown, the renderer fires `OnControllerForceExit()`. `GuiRemoteController` first sets `connectionForcedToStop = true` and then calls `remoteWindow.Hide(true)`. The flag makes the close path bypass the `BeforeClosing` cancellation check.
|
||||
|
||||
### Run Loop (RunOneCycle)
|
||||
|
||||
@@ -104,6 +104,8 @@ The renderer side fires `OnControllerRequestExit()`:
|
||||
|
||||
The timer-driven rendering is crucial: `InvokeGlobalTimer()` causes `GuiHostedController` to decide whether to render, which calls into `GuiRemoteGraphicsRenderTarget`.
|
||||
|
||||
When the hosted controller determines that rendering has become idle, `GuiRemoteGraphicsRenderTarget::HostedRenderingIdle()` queues `RequestRendererIdle()`. This is an idle hint rather than a rendering frame.
|
||||
|
||||
## GuiRemoteWindow: Virtual Native Window
|
||||
|
||||
`GuiRemoteWindow` implements `INativeWindow` for one virtual window.
|
||||
@@ -237,7 +239,7 @@ Paragraph styling uses a run-based system with three layers:
|
||||
This is the core synchronization method, called before any query. It returns `false` when the paragraph is unavailable (`id == -1` or no render target) or when a synchronous submit reports disconnection, allowing callers to return conservative defaults instead of using stale remote state:
|
||||
1. `EnsureRequestedRenderersCreated()` — ensures the paragraph element exists on the renderer side
|
||||
2. Merge text and inline object runs into `stagedRuns`
|
||||
3. `DiffRuns(committedRuns, stagedRuns, desc)` — compute diff between last committed and current state
|
||||
3. `DiffRuns(committedRuns, stagedRuns, desc)` — compute diff between last committed and current state; it requires every old run range to remain fully covered by the new run ranges and fails with `CHECK_ERROR` if that invariant is violated
|
||||
4. Send `RequestRendererUpdateElement_DocumentParagraph(desc)` with the diff
|
||||
5. `Submit()` synchronously — the response includes the new `documentSize`
|
||||
6. If still connected, store `cachedSize` from response, remove stale cached inline-object bounds reported by `removedInlineObjects`, move `stagedRuns` to `committedRuns`, and mark `remoteParagraphCreated = true`
|
||||
@@ -321,7 +323,7 @@ The core-side stack uses:
|
||||
2. `GuiRemoteProtocolAsyncJsonChannel` when core/channel separation is needed. It wraps an `IJsonChannel`, queues outgoing packages, queues received events for `ProcessRemoteEvents()`, stores responses by request id, and uses `connectionCounter` plus `PendingRequestGroup` to keep blocking `Submit()` calls consistent across disconnects.
|
||||
|
||||
The renderer-side stack uses:
|
||||
1. `GuiRemoteProtocolAsyncJsonChannelRenderer` when network packages can arrive before the native GacUI window is ready. It queues received packages until `SetInvokeInMainThread(...)` installs an `IGuiRemoteProtocolAsyncRendererInvoker`, then drains them on the renderer UI thread. Its message version prevents callbacks captured by an old reader from running after the reader is replaced.
|
||||
1. `GuiRemoteProtocolAsyncJsonChannelRenderer` when network packages can arrive before the native GacUI window is ready. It queues received packages until `SetInvokeInMainThread(...)` installs an `IGuiRemoteProtocolAsyncRendererInvoker`, then drains them on the renderer UI thread. Calling `Detach()` clears the reader, increments `messageVersion`, and prevents callbacks queued before detachment from running after a later reader installation.
|
||||
2. `GuiRemoteProtocolRendererChannel` to bridge the renderer JSON channel to a concrete renderer `IGuiRemoteProtocol` implementation and to serialize renderer events/responses back to `GacUIRemoteProtocolCoreClientId`. Construct it with the renderer-side `IJsonChannel` and the `IGuiRemoteProtocol`.
|
||||
|
||||
## Image Service
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#### Remote Protocol Renderer and Serialization
|
||||
|
||||
The remote protocol renderer side receives protocol messages from the core side and translates them into native window operations and graphics rendering. The serialization and channel infrastructure provides composable layers that convert between typed protocol calls and transport-ready strings, enabling GacUI applications to run across process boundaries over any user-provided transport (named pipe, HTTP, WebSocket, etc.).
|
||||
The remote protocol renderer side receives protocol messages from the core side and translates them into native window operations and graphics rendering. The serialization and channel infrastructure provides composable layers that convert between typed protocol calls and Parser2 JSON node packages, enabling GacUI applications to run across process boundaries over any user-provided transport (named pipe, HTTP, WebSocket, etc.).
|
||||
|
||||
## GuiRemoteRendererSingle
|
||||
|
||||
@@ -22,14 +22,16 @@ The remote protocol renderer side receives protocol messages from the core side
|
||||
- `renderingDom` / `renderingDomIndex`: The rendering DOM tree received from the core side, used for on-screen rendering and hit testing.
|
||||
- `solidLabelMeasurings` / `fontHeightMeasurings`: Caches and measurement tracking for text elements, supporting the measurement feedback loop.
|
||||
- `pendingMouseMove`, `pendingHWheel`, `pendingVWheel`, `pendingKeyAutoDown`, `pendingWindowBoundsUpdate`: Accumulation fields for coalescing high-frequency events before sending.
|
||||
- `disconnectingFromCore`: Prevents further protocol requests and events after the renderer disconnects from the core.
|
||||
- `stoppedByFatalError` / `fatalError` / `fatalMaskElement` / `fatalTextElement`: Retain-mode state and graphics for displaying a fatal-error overlay instead of immediately closing the renderer.
|
||||
|
||||
### Source File Organization
|
||||
|
||||
- `GuiRemoteRendererSingle.cpp`: Construction, destruction, main window registration, connection lifecycle (`Opened`, `BeforeClosing`, `Closed`), screen/config management.
|
||||
- `GuiRemoteRendererSingle.cpp`: Construction, destruction, main window registration, connection lifecycle (`Opened`, `BeforeClosing`, `AfterClosing`, `Closed`), screen/config management, core disconnection, and fatal-error retention.
|
||||
- `GuiRemoteRendererSingle_Controller.cpp`: Controller-level requests — `RequestControllerGetFontConfig`, `RequestControllerGetScreenConfig`, `RequestControllerConnectionEstablished`, `RequestControllerConnectionStopped`.
|
||||
- `GuiRemoteRendererSingle_MainWindow.cpp`: Window style notifications — `RequestWindowNotifySetBounds`, `RequestWindowNotifySetTitle`, `RequestWindowNotifySetEnabled`, `RequestWindowNotifyShow`, etc.
|
||||
- `GuiRemoteRendererSingle_IO.cpp`: IO requests (global shortcuts, mouse capture, key state queries) and native-to-protocol input event conversion. Contains `SendAccumulatedMessages()` for batching high-frequency events.
|
||||
- `GuiRemoteRendererSingle_Rendering.cpp`: Core rendering pipeline — element creation/destruction (`RequestRendererCreated`, `RequestRendererDestroyed`), begin/end rendering, DOM rendering (`Render` recursive traversal), hit testing, and `GlobalTimer`/`Paint` driven refresh.
|
||||
- `GuiRemoteRendererSingle_Rendering.cpp`: Core rendering pipeline — element creation/destruction (`RequestRendererCreated`, `RequestRendererDestroyed`), begin/end rendering, DOM rendering (`RenderDom` recursive traversal), fatal-overlay rendering, hit testing, and `GlobalTimer`/`Paint` driven refresh.
|
||||
- `GuiRemoteRendererSingle_Rendering_Elements.cpp`: Property updates on ordinary graphics elements (solid border, sink border, splitter, background, gradient, inner shadow, polygon).
|
||||
- `GuiRemoteRendererSingle_Rendering_Label.cpp`: Solid label measurement and property updates.
|
||||
- `GuiRemoteRendererSingle_Rendering_Image.cpp`: Image creation, metadata, and image frame element updates.
|
||||
@@ -43,12 +45,12 @@ The core sends `RequestRendererRenderDom` or `RequestRendererRenderDomDiff` to u
|
||||
|
||||
`RequestRendererRenderDom` installs a full `RenderingDom`, rebuilds `renderingDomIndex`, and sets `needRefresh = true`. `RequestRendererRenderDomDiff` requires an existing DOM, applies `UpdateDomInplace(renderingDom, renderingDomIndex, diffs)`, and also sets `needRefresh = true`.
|
||||
|
||||
Actual painting happens in either `RequestRendererEndRendering` or `GlobalTimer()`: when `needRefresh` is true and refresh is no longer suppressed, `ForceRender()` recursively traverses `renderingDom`, renders each element in order with clipping, redraws the native window, and handles render-target resize/lost-device failures by resizing/recreating the target and scheduling another refresh. `RequestRendererEndRendering` clears suppression and can force the completed frame to render before returning `RespondRendererEndRendering(id, elementMeasurings)`. `GlobalTimer()` also flushes accumulated IO events, drives caret blinking, and renders any pending refresh not already handled at frame end.
|
||||
Actual painting happens in either `RequestRendererEndRendering` or `GlobalTimer()`: when `needRefresh` is true and refresh is no longer suppressed, `ForceRender()` calls `RenderDom` to recursively traverse `renderingDom`, renders each element in order with clipping, optionally draws the fatal-error overlay, redraws the native window, and handles render-target resize/lost-device failures by resizing/recreating the target and scheduling another refresh. `RequestRendererEndRendering` clears suppression and can force the completed frame to render before returning `RespondRendererEndRendering(id, elementMeasurings)`. While connected, `GlobalTimer()` also flushes accumulated IO events and drives caret blinking; after disconnection it clears pending core events but can continue rendering a retained fatal-error overlay. `RequestRendererIdle()` is an idle hint and is intentionally ignored by this renderer.
|
||||
|
||||
### Event Forwarding
|
||||
|
||||
`GuiRemoteRendererSingle` implements all `INativeWindowListener` mouse/keyboard callbacks to forward OS events as protocol events:
|
||||
- **Discrete events** (button clicks, key presses): Sent immediately via `events->OnIOMouseLeft`, `events->OnIOKeyDown`, etc.
|
||||
- **Discrete events** (button clicks, key presses): Sent immediately via `events->OnIOButtonDown`, `events->OnIOButtonUp`, `events->OnIOButtonDoubleClick`, `events->OnIOKeyDown`, etc. Mouse-button events carry an `IOMouseInfoWithButton` whose `button` is `IOMouseButton::Left`, `Right`, or `Middle`.
|
||||
- **High-frequency events** (mouse move, wheel, key auto-repeat): Accumulated and coalesced:
|
||||
- `pendingMouseMove`: Only the latest mouse position is kept.
|
||||
- `pendingHWheel` / `pendingVWheel`: Wheel deltas are summed across frames.
|
||||
@@ -56,6 +58,8 @@ Actual painting happens in either `RequestRendererEndRendering` or `GlobalTimer(
|
||||
- `SendAccumulatedMessages()` is called from `GlobalTimer()` to flush these accumulated events.
|
||||
- **Window lifecycle events** (`Opened`, `BeforeClosing`, `Moved`, `DpiChanged`): Translated to protocol events like `OnControllerConnect`, `OnControllerRequestExit`, `OnWindowBoundsUpdated`.
|
||||
|
||||
All outgoing callbacks and responses are guarded by `CanSendEvents()`. `DisconnectFromCore()` marks the renderer as disconnecting, releases mouse capture, unregisters global shortcuts, and clears all accumulated events so no stale input is sent afterward.
|
||||
|
||||
### Hit Testing
|
||||
|
||||
Hit testing is performed locally in the renderer by traversing the rendering DOM tree via `HitTestInternal`. Each DOM node may have `hitTestResult` and `cursor` attributes set by the core side. The renderer walks the tree and finds the matching node for a given point. This avoids round-trips — hit testing stays entirely renderer-side.
|
||||
@@ -113,7 +117,7 @@ Protocol types are code-generated from `Protocol/*.txt` files into `GuiRemotePro
|
||||
|
||||
`GuiRemoteProtocolAsyncJsonChannel` is the core-side async wrapper around an `IJsonChannel`. It queues outgoing packages, queues incoming events for `ProcessRemoteEvents()`, stores incoming responses by request id, and blocks `BatchWrite(disconnected)` until the current `PendingRequestGroup` is satisfied or disconnected. `connectionCounter` and `connectionClientId` protect pending requests when channel events arrive after disconnect/reconnect boundaries.
|
||||
|
||||
`GuiRemoteProtocolAsyncJsonChannelRenderer` is the renderer-side async wrapper. It queues received packages and schedules `ProcessRemoteMessages()` through an `IGuiRemoteProtocolAsyncRendererInvoker`. Before `SetInvokeInMainThread(...)` is called by renderer `GuiMain`, packages are cached. After the invoker is installed, they are drained on the renderer UI thread. A `messageVersion` stamp prevents messages captured by an old reader from running after the channel reader is replaced or detached.
|
||||
`GuiRemoteProtocolAsyncJsonChannelRenderer` is the renderer-side async wrapper. It queues received packages and schedules `ProcessRemoteMessages()` through an `IGuiRemoteProtocolAsyncRendererInvoker`. Before `SetInvokeInMainThread(...)` is called by renderer `GuiMain`, packages are cached. After the invoker is installed, they are drained on the renderer UI thread. `Initialize(reader)` requires a non-null reader; `Detach()` explicitly clears it, increments `messageVersion`, and drops queued work. The version check prevents callbacks queued before `Detach()` from running after detachment or a later reader installation.
|
||||
|
||||
## Demo Project Pair
|
||||
|
||||
@@ -137,15 +141,15 @@ Located at `Test/GacUISrc/RemotingTest_Core/`. Accepts `/Pipe` or `/Http` argume
|
||||
|
||||
Located at `Test/GacUISrc/RemotingTest_Rendering_Win32/`. Accepts `/Pipe` or `/Http` arguments to start as a named-pipe or HTTP client.
|
||||
|
||||
**Protocol stack setup** (`StartClient<TClient>` in `GuiMain.cpp`):
|
||||
1. Creates `RemotingTestChannelClient`, derived from `GuiRemoteProtocolChannelClient`, over a named-pipe or HTTP `INetworkProtocolClient`.
|
||||
**Protocol stack setup** (`StartClient` in `GuiMain.cpp`; this function is not a template):
|
||||
1. Receives a named-pipe or HTTP `INetworkProtocolClient` and creates `RemotingTestChannelClient`, derived from `GuiRemoteProtocolChannelClient`, over it.
|
||||
2. Creates `GuiRemoteProtocolAsyncJsonChannelRenderer` over the client's protocol channel.
|
||||
3. Creates `GuiRemoteRendererSingle` and `GuiRemoteProtocolRendererChannel(&asyncRendererChannel, &remoteRenderer)`.
|
||||
4. Waits for the server, then calls `SetupRawWindowsDirect2DRenderer()` to run the native window event loop.
|
||||
5. In `GuiMain()`, creates the native window, registers it with `GuiRemoteRendererSingle`, installs `GuiMainAsyncRendererInvoker` through `asyncChannel->SetInvokeInMainThread(&invoker)`, and runs the window service.
|
||||
6. On exit, clears the invoker, unregisters the main window, stops the network connection, and clears stack-owned renderer/channel pointers.
|
||||
|
||||
`RemotingTestChannelClient` handles remote fatal errors by showing a native error message and calling `GuiRemoteRendererSingle::ForceExitByFatelError()`. On disconnect, it detaches the async renderer channel with `Initialize(nullptr)` and forces renderer exit unless the disconnect was already caused by a fatal error.
|
||||
`RemotingTestChannelClient` records only the first fatal error and queues a native Yes/No prompt asking whether to close the renderer. A core read error uses the core-error title. A fatal local transport error uses the renderer-transport title and first calls `GuiRemoteRendererSingle::RequestCoreForceExitByFatalError()`. Choosing Yes calls `ForceExitByFatelError()`; choosing No calls `RetainByFatalError(message)`, keeps the native renderer window open with a `[STOPPED]` title and fatal overlay, and exposes the error through the renderer automation service. On disconnect, the client calls `GuiRemoteProtocolAsyncJsonChannelRenderer::Detach()` and forces renderer exit only when no fatal error has already been claimed.
|
||||
|
||||
### Protocol Stack Direction
|
||||
|
||||
|
||||
@@ -130,16 +130,17 @@ Each test case follows a standard pattern:
|
||||
|
||||
### GacUIUnitTest_StartFast_WithResourceAsText
|
||||
|
||||
This template function is the most commonly used entry point. It internally calls `GacUIUnitTest_LinkGuiMainProxy` to inject:
|
||||
1. Sending `OnControllerConnect` with `ControllerGlobalConfig` to the protocol.
|
||||
2. Registering a theme (e.g., `DarkSkin`).
|
||||
3. Compiling the GacUI XML resource via `GacUIUnitTest_CompileAndLoad`.
|
||||
4. Creating the main window from the resource via `Value::Create(windowTypeFullName)`.
|
||||
5. Calling `previousMainProxy(protocol, context)` to let the test register its frame callbacks.
|
||||
6. Running the application via `GetApplication()->Run(window)`.
|
||||
7. Unregistering the theme on cleanup.
|
||||
|
||||
It also saves the compiled Workflow script text as a snapshot file (`[x64].txt` or `[x86].txt`) for Workflow generation stability verification.
|
||||
This template function is the most commonly used entry point. `GacUIUnitTest_StartFast_WithResourceAsText` and `GacUIUnitTest_Start_WithResourceAsText` install two proxy layers. When `GuiMain` invokes the resulting proxy chain, it performs these steps in order:
|
||||
1. Compile and load the GacUI XML resource via `GacUIUnitTest_CompileAndLoad`.
|
||||
2. Save the compiled Workflow script text as a snapshot file (`[x64].txt` or `[x86].txt`) for Workflow generation stability verification.
|
||||
3. Send `OnControllerConnect` with `ControllerGlobalConfig` to the protocol.
|
||||
4. Register the requested theme type.
|
||||
5. Call the optional `GacUIUnitTest_Installer::initialize` callback.
|
||||
6. Create the main window via `Value::Create(windowTypeFullName)`.
|
||||
7. Call the optional `GacUIUnitTest_Installer::installWindow` callback, then center the window with `MoveToScreenCenter()`.
|
||||
8. Call `previousMainProxy(protocol, context)` to let the test register its frame callbacks.
|
||||
9. Run the application via `GetApplication()->Run(window)`.
|
||||
10. Call the optional `GacUIUnitTest_Installer::finalize` callback and unregister the theme during cleanup.
|
||||
|
||||
### Start Functions
|
||||
|
||||
@@ -276,19 +277,20 @@ Overloads accept either `GuiGraphicsComposition*` or `GuiControl*`.
|
||||
- `MouseMove(location)` — sends `OnIOMouseMoving`. If mouse was previously unset, first sends `OnIOMouseEntered`.
|
||||
- `_LDown(location)` / `_LUp(location)` — low-level left button down/up. `_LDown` calls `MouseMove` if position changed, then `UseEvents().OnIOButtonDown({Left, ...})`.
|
||||
- `LClick(location)` — `_LDown` followed by `_LUp`.
|
||||
- `LDBClick(location)` — two `LClick` calls (the framework detects double click from timing).
|
||||
- `LDBClick(location)` — sends a normal down/up pair, an explicit `OnIOButtonDoubleClick`, and the final up event.
|
||||
- Analogous `RClick`, `MClick`, `RDBClick`, `MDBClick` for right and middle buttons.
|
||||
- `WheelUp(jumps)` / `WheelDown(jumps)` — `OnIOVWheel` with delta scaled by 120 per jump.
|
||||
- `HWheelLeft(jumps)` / `HWheelRight(jumps)` — `OnIOHWheel` similarly.
|
||||
|
||||
### Key and Character Input Methods
|
||||
|
||||
- `KeyDown(key)` / `KeyUp(key)` — sends `OnIOKeyDown` / `OnIOKeyUp`. Tracks `pressingKeys` state. Special handling for `VKEY::KEY_CAPITAL` toggles `capslockToggled`.
|
||||
- `KeyPress(key)` — `KeyDown` followed by `KeyUp`.
|
||||
- `_KeyDown(key)` / `_KeyUp(key)` — sends `OnIOKeyDown` / `OnIOKeyUp`. Tracks `pressingKeys` state. Special handling for `VKEY::KEY_CAPITAL` toggles `capslockToggled`.
|
||||
- `_KeyDownRepeat(key)` — sends an auto-repeat `OnIOKeyDown` for a key that is already pressed.
|
||||
- `KeyPress(key)` — `_KeyDown` followed by `_KeyUp`.
|
||||
- `KeyPress(key, ctrl, shift, alt)` — wraps the key press with modifier key down/up events.
|
||||
- `TypeString(text)` — sends a sequence of `OnIOChar` events for each character via `MakeCharInfo`, without synthesizing key down/up events.
|
||||
|
||||
Keyboard helpers target the currently focused control. Focus the target control in the frame (for example with `SetFocused()` or a click) before using `TypeString`, `KeyPress`, `KeyDown`, or `KeyUp`.
|
||||
Keyboard helpers target the currently focused control. Focus the target control in the frame (for example with `SetFocused()` or a click) before using `TypeString`, `KeyPress`, `_KeyDown`, or `_KeyUp`.
|
||||
|
||||
### Event Flow Through the Pipeline
|
||||
|
||||
@@ -311,6 +313,6 @@ If an IO action triggers a blocking function (like `ShowDialog`), the frame call
|
||||
|
||||
Each call to `GacUIUnitTest_Start` runs one complete application lifecycle (from `GuiMain()` to window close). Typically each `TEST_CASE` calls the full `GacUIUnitTest_SetGuiMainProxy` → `GacUIUnitTest_Start` sequence independently.
|
||||
|
||||
Within a single application instance, multiple windows can be created inside frame callbacks using `Value::Create(L"namespace::ClassName")` and shown with `Show()`, `ShowModal()`, or `ShowDialog()`.
|
||||
Within a single application instance, multiple windows can be created inside frame callbacks using `Value::Create(L"namespace::ClassName")` and shown with `Show()`, `ShowWithOwner()`, or `ShowModal()`. `ShowDialog()` belongs to dialog component classes such as `GuiOpenFileDialog`, not to `GuiWindow`.
|
||||
|
||||
`GacUIUnitTest_LinkGuiMainProxy` can be called multiple times before `GacUIUnitTest_Start`, chaining multiple setup layers (e.g., one for resources, one for theme, one for additional initialization).
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
The `vl::inter_process` namespace is designed for inter-process communication. Some abstractions are general enough to carry messages inside one process or across a custom transport, but the lifecycle, error handling, connection model and naming are intended for communication between processes.
|
||||
|
||||
The built-in `INetworkProtocol*` transports should be treated as reference implementations, validation targets and demo-friendly options. The current concrete `NamedPipeServer` / `NamedPipeClient` and `HttpServer` / `HttpClient` implementations are Windows-only, and they are not meant to be the only production transport choice for every application.
|
||||
The built-in `INetworkProtocol*` transports should be treated as reference implementations, validation targets and demo-friendly options. The current concrete `vl::inter_process::named_pipe::NamedPipeServer` / `vl::inter_process::named_pipe::NamedPipeClient` and `vl::inter_process::windows_http::HttpServer` / `vl::inter_process::windows_http::HttpClient` implementations are Windows-only, and they are not meant to be the only production transport choice for every application.
|
||||
|
||||
This distinction is especially important for HTTP. `HttpServer` and `HttpClient` are raw `INetworkProtocol*` reference/demo transports, while `HttpServerApi` and `HttpClientApi` are lower-level Windows HTTP helper utilities. When a Windows feature needs HTTP request/response behavior directly, the helper utilities can still be used without adopting the inter-process raw transport as the feature contract.
|
||||
This distinction is especially important for HTTP. `vl::inter_process::windows_http::HttpServer` and `vl::inter_process::windows_http::HttpClient` are raw `INetworkProtocol*` reference/demo transports, while `vl::inter_process::windows_http::HttpServerApi` and `vl::inter_process::windows_http::HttpClientApi` are lower-level Windows HTTP helper utilities. When a Windows feature needs HTTP request/response behavior directly, the helper utilities can still be used without adopting the inter-process raw transport as the feature contract.
|
||||
|
||||
When the built-in raw protocol implementation does not fit the platform, security model, deployment shape, performance target or reconnection behavior of a feature, implement a custom `INetworkProtocolServer`, `INetworkProtocolClient` and `INetworkProtocolConnection`. The default channel bridge can still be reused as long as the custom raw transport follows the `INetworkProtocol*` contract.
|
||||
|
||||
@@ -18,6 +18,8 @@ The inter-process communication APIs in `vl::inter_process` are layered:
|
||||
- The channel layer, represented by `IChannelServer<TPackage>`, `IChannelClient<TPackage>`, `IChannel<TPackage>` and `IChannelReader<TPackage>`, builds typed named channels on top of a connected client id model.
|
||||
- The default bridge, represented by `NetworkProtocolChannelServer<TPackage, TSerialization, TServerBase>`, `NetworkProtocolChannelClient<TPackage, TSerialization>` and `NetworkProtocolLocalChannelClient<TPackage, TSerialization>`, serializes channel batches into raw `WString` messages over an `INetworkProtocol*` transport.
|
||||
|
||||
The transport-agnostic contracts and channel templates stay directly in `vl::inter_process`. Windows concrete implementations are grouped by feature: named-pipe types are in `vl::inter_process::named_pipe`, and HTTP transport, helper, request, response and error types are in `vl::inter_process::windows_http`.
|
||||
|
||||
The interfaces are transport-agnostic. Use the abstract interfaces in portable feature code, and bind them to concrete transports at application composition boundaries.
|
||||
|
||||
## Choosing the Layer
|
||||
@@ -202,7 +204,7 @@ Channel message semantics:
|
||||
|
||||
`NetworkProtocolChannelServer<TPackage, TSerialization, TServerBase>` combines the channel server with a raw protocol server.
|
||||
|
||||
- `TServerBase` is a concrete `INetworkProtocolServer` implementation, such as `NamedPipeServer` or `HttpServer`.
|
||||
- `TServerBase` is a concrete `INetworkProtocolServer` implementation, such as `vl::inter_process::named_pipe::NamedPipeServer` or `vl::inter_process::windows_http::HttpServer`.
|
||||
- The class inherits `TServerBase`, `IChannelServer<TPackage>` and the private local-client server interface.
|
||||
- `Start` records the channel server as started, then calls `TServerBase::Start`.
|
||||
- Raw `OnClientConnected(INetworkProtocolConnection*)` rejects connections before start or after stop, creates a pending connection context, installs a raw callback, starts raw reading, and waits for the channel handshake.
|
||||
@@ -245,11 +247,11 @@ To build a typed channel application over a raw transport:
|
||||
|
||||
## Current Windows-Only Raw Transports
|
||||
|
||||
The built-in `NamedPipeServer` / `NamedPipeClient` and `HttpServer` / `HttpClient` classes are currently Windows-only implementations of the raw protocol interfaces. They should not be treated as cross-platform transport classes.
|
||||
The built-in `vl::inter_process::named_pipe::NamedPipeServer` / `vl::inter_process::named_pipe::NamedPipeClient` and `vl::inter_process::windows_http::HttpServer` / `vl::inter_process::windows_http::HttpClient` classes are currently Windows-only implementations of the raw protocol interfaces. They should not be treated as cross-platform transport classes.
|
||||
|
||||
### `NamedPipeServer` and `NamedPipeClient`
|
||||
### `vl::inter_process::named_pipe::NamedPipeServer` and `vl::inter_process::named_pipe::NamedPipeClient`
|
||||
|
||||
`NamedPipeConnection` implements `INetworkProtocolConnection`. `NamedPipeClient` derives from `NamedPipeConnection` and implements `INetworkProtocolClient`. `NamedPipeServer` implements `INetworkProtocolServer`.
|
||||
`vl::inter_process::named_pipe::NamedPipeConnection` implements `INetworkProtocolConnection`. `vl::inter_process::named_pipe::NamedPipeClient` derives from `vl::inter_process::named_pipe::NamedPipeConnection` and implements `INetworkProtocolClient`. `vl::inter_process::named_pipe::NamedPipeServer` implements `INetworkProtocolServer`.
|
||||
|
||||
- `NamedPipeServer::Start` begins overlapped named-pipe accepting.
|
||||
- `NamedPipeServer` keeps both accepted `NamedPipeConnection` objects and pending `ConnectNamedPipe` operations.
|
||||
@@ -262,9 +264,9 @@ The built-in `NamedPipeServer` / `NamedPipeClient` and `HttpServer` / `HttpClien
|
||||
- `NamedPipeConnection::Stop` cancels pending overlapped pipe I/O, unregisters pending waits, waits for pending callbacks, and closes handles.
|
||||
- `NamedPipeServer::Stop` drains both pending accepts and accepted connections.
|
||||
|
||||
### `HttpServer` and `HttpClient`
|
||||
### `vl::inter_process::windows_http::HttpServer` and `vl::inter_process::windows_http::HttpClient`
|
||||
|
||||
`HttpClient` implements both `INetworkProtocolConnection` and `INetworkProtocolClient`. `HttpServer` derives from `HttpServerApi` and implements `INetworkProtocolServer`. This is also a Windows-only reference/demo implementation.
|
||||
`vl::inter_process::windows_http::HttpClient` implements both `INetworkProtocolConnection` and `INetworkProtocolClient`. `vl::inter_process::windows_http::HttpServer` derives from `vl::inter_process::windows_http::HttpServerApi` and implements `INetworkProtocolServer`. This is also a Windows-only reference/demo implementation.
|
||||
|
||||
The HTTP protocol uses three routes:
|
||||
|
||||
@@ -282,7 +284,7 @@ The HTTP protocol uses three routes:
|
||||
- `/Request` failures are retried while the client is still running.
|
||||
- `HttpClient::Stop` stops the underlying `HttpClientApi`, signals any waiting `WaitForServer`, and reports `OnDisconnected`.
|
||||
|
||||
`HttpServer` and `HttpServerConnection` behavior:
|
||||
`vl::inter_process::windows_http::HttpServer` and `vl::inter_process::windows_http::HttpServerConnection` behavior:
|
||||
|
||||
- On `/Connect`, `HttpServer` creates a `HttpServerConnection`, assigns a GUID, calls `OnClientConnected`, and returns the per-connection request and response URLs, or rejects with an HTTP error response.
|
||||
- `HttpServerConnection::BeginReadingLoopUnsafe` is a no-op because `HttpServerApi` owns the receive loop.
|
||||
@@ -295,7 +297,7 @@ The HTTP protocol uses three routes:
|
||||
|
||||
## Windows HTTP Helper Layer
|
||||
|
||||
`HttpClientApi` and `HttpServerApi` are reusable Windows helper classes used by `HttpClient` and `HttpServer`. Use `HttpClient` and `HttpServer` only when the reference/demo raw `INetworkProtocol*` transport is the desired shape; use the helper APIs directly when a Windows-specific feature needs lower-level HTTP request/response behavior.
|
||||
`vl::inter_process::windows_http::HttpClientApi` and `vl::inter_process::windows_http::HttpServerApi` are reusable Windows helper classes used by `vl::inter_process::windows_http::HttpClient` and `vl::inter_process::windows_http::HttpServer`. The supporting `HttpRequest`, `HttpResponse`, `HttpError` and `HttpServerResponse` value types are in the same `vl::inter_process::windows_http` namespace. Use the raw transport classes only when the reference/demo `INetworkProtocol*` shape is desired; use the helper APIs directly when a Windows-specific feature needs lower-level HTTP request/response behavior.
|
||||
|
||||
`HttpClientApi` owns one WinHTTP session and connection for one host and port.
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ If you want a remote protocol renderer that still work with other components of
|
||||
The [GacJS](https://github.com/vczh-libraries/GacJS) also implements a GacUI renderer in a browser, connecting to a GacUI application via HTTP protocol. But the HTTP protocol implementation is not official, it is for demo only, no security guarantee is provided.
|
||||
|
||||
GacUI provides a JSON channel layer for the remote protocol, implemented with **vl::inter_process**. A C++ core application usually starts a **GuiRemoteProtocolNetworkChannelServer**, connects itself through **GuiRemoteProtocolLocalChannelClient**, and waits for a renderer connected through **GuiRemoteProtocolChannelClient**. The underlying data-transmission implementation is still selected by the application. Current Windows examples use:
|
||||
- **vl::inter_process::NamedPipeServer** and **vl::inter_process::NamedPipeClient**.
|
||||
- **vl::inter_process::HttpServer** and **vl::inter_process::HttpClient**.
|
||||
- **vl::inter_process::named_pipe::NamedPipeServer** and **vl::inter_process::named_pipe::NamedPipeClient**.
|
||||
- **vl::inter_process::windows_http::HttpServer** and **vl::inter_process::windows_http::HttpClient**.
|
||||
|
||||
A different data-transmission implementation should be added by implementing the **vl::inter_process****INetworkProtocolConnection**, **INetworkProtocolCallback**, **INetworkProtocolClient** and **INetworkProtocolServer** interfaces. They are named network protocol interfaces, but the implementation does not have to be a network protocol; stdio, DLL function calls, shared memory, sockets or any other message transport can fit this layer. so the GacUI JSON channel classes can stay unchanged. This part will be covered in [Remote Protocol Channel Layer](../.././gacui/modes/remote_communication.md).
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ void GuiMain()
|
||||
int StartNamedPipeRemoteRenderer()
|
||||
{
|
||||
auto jsonParser = Ptr(new glr::json::Parser);
|
||||
auto networkClient = Ptr(new inter_process::NamedPipeClient(L"GacUIRemoteProtocolNamedPipe"));
|
||||
auto networkClient = Ptr(new inter_process::named_pipe::NamedPipeClient(L"GacUIRemoteProtocolNamedPipe"));
|
||||
|
||||
GuiRemoteProtocolChannelClient channelClient(networkClient, jsonParser);
|
||||
GuiRemoteProtocolAsyncJsonChannelRenderer asyncRendererChannel(channelClient.GetProtocolChannel());
|
||||
|
||||
@@ -6,7 +6,7 @@ The layers are:
|
||||
- IGuiRemoteProtocol and IGuiRemoteEventProcessor: the strongly typed GacUI remote protocol.
|
||||
- GuiRemoteProtocolCoreChannel and GuiRemoteProtocolRendererChannel: conversion between the strongly typed protocol and JSON channel packages.
|
||||
- GuiRemoteProtocolNetworkChannelServer, GuiRemoteProtocolLocalChannelClient and GuiRemoteProtocolChannelClient: the vl::inter_process JSON channel bridge.
|
||||
- NamedPipeServer, NamedPipeClient, HttpServer, HttpClient, or custom implementations of the INetworkProtocolConnection, INetworkProtocolCallback, INetworkProtocolClient and INetworkProtocolServer interfaces: the underlying data-transmission implementation.
|
||||
- **vl::inter_process::named_pipe::NamedPipeServer**, **vl::inter_process::named_pipe::NamedPipeClient**, **vl::inter_process::windows_http::HttpServer**, **vl::inter_process::windows_http::HttpClient**, or custom implementations of the INetworkProtocolConnection, INetworkProtocolCallback, INetworkProtocolClient and INetworkProtocolServer interfaces: the underlying data-transmission implementation.
|
||||
|
||||
The JSON channel package type is Ptr\<glr::json::JsonNode\>. The remote protocol channel name is GacUIRemoteProtocolChannelName, whose value is GacUIRemoteProtocol. The in-process core client should be assigned GacUIRemoteProtocolCoreClientId. Renderer-side packages are sent to that core client id, and core-side packages are sent to the renderer client id learned from ControllerConnect.
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
A remote protocol core application is a normal GacUI application that calls SetupRemoteNativeController. It becomes headless and is forced into hosted mode. The core does not draw to an OS native window; it sends JSON remote protocol packages through a vl::inter_process channel.
|
||||
|
||||
The standard C++ path is small:
|
||||
- Start a GuiRemoteProtocolNetworkChannelServer\<TServerBase\> over an INetworkProtocolServer implementation such as NamedPipeServer or HttpServer.
|
||||
- Start a GuiRemoteProtocolNetworkChannelServer\<TServerBase\> over an INetworkProtocolServer implementation such as **vl::inter_process::named_pipe::NamedPipeServer** or **vl::inter_process::windows_http::HttpServer**.
|
||||
- Connect the core to that server with GuiRemoteProtocolLocalChannelClient.
|
||||
- Wrap the core channel with GuiRemoteProtocolAsyncJsonChannel and GuiRemoteProtocolCoreChannel.
|
||||
- Pass the protocol, usually after GuiRemoteProtocolFilter and GuiRemoteProtocolDomDiffConverter, to SetupRemoteNativeController.
|
||||
@@ -19,9 +19,9 @@ using namespace vl::presentation::remoteprotocol::channeling;
|
||||
using namespace vl::presentation::remoteprotocol::repeatfiltering;
|
||||
|
||||
class NamedPipeRemoteCoreServer
|
||||
: public GuiRemoteProtocolNetworkChannelServer<inter_process::NamedPipeServer>
|
||||
: public GuiRemoteProtocolNetworkChannelServer<inter_process::named_pipe::NamedPipeServer>
|
||||
{
|
||||
using Base = GuiRemoteProtocolNetworkChannelServer<inter_process::NamedPipeServer>;
|
||||
using Base = GuiRemoteProtocolNetworkChannelServer<inter_process::named_pipe::NamedPipeServer>;
|
||||
|
||||
EventObject rendererConnected;
|
||||
|
||||
|
||||
@@ -177,11 +177,11 @@ using namespace vl;
|
||||
using namespace vl::inter_process;
|
||||
|
||||
class ChatServer
|
||||
: public NetworkProtocolChannelServer<WString, WStringListSerializer, NamedPipeServer>
|
||||
: public NetworkProtocolChannelServer<WString, WStringListSerializer, named_pipe::NamedPipeServer>
|
||||
{
|
||||
public:
|
||||
ChatServer(const WString& pipeName)
|
||||
: NetworkProtocolChannelServer<WString, WStringListSerializer, NamedPipeServer>(pipeName)
|
||||
: NetworkProtocolChannelServer<WString, WStringListSerializer, named_pipe::NamedPipeServer>(pipeName)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -206,28 +206,28 @@ The concrete raw protocol implementations currently provided by VlppOS are Windo
|
||||
|
||||
### Named Pipe Transport
|
||||
|
||||
**NamedPipeServer** implements **INetworkProtocolServer**. **NamedPipeClient** derives from **NamedPipeConnection** and implements **INetworkProtocolClient**. **NamedPipeConnection** implements **INetworkProtocolConnection**.
|
||||
**vl::inter_process::named_pipe::NamedPipeServer** implements **INetworkProtocolServer**. **vl::inter_process::named_pipe::NamedPipeClient** derives from **vl::inter_process::named_pipe::NamedPipeConnection** and implements **INetworkProtocolClient**. **vl::inter_process::named_pipe::NamedPipeConnection** implements **INetworkProtocolConnection**.
|
||||
|
||||
The server starts overlapped named-pipe accepting in **Start** and owns both accepted connections and pending accepts. The client opens **\\.\pipe\NAME**, waits for the server and switches the pipe into message-read mode. **SendString** frames one **WString** with length data. The implementation chunks writes because a Windows named pipe message is limited to 64K bytes. Broken-pipe cases become disconnection events, and fatal local pipe failures become **OnLocalError** followed by disconnection.
|
||||
|
||||
### HTTP Transport
|
||||
|
||||
**HttpServer** derives from **HttpServerApi** and implements **INetworkProtocolServer**. **HttpClient** implements both **INetworkProtocolClient** and **INetworkProtocolConnection**. This transport is a raw protocol implementation over Windows HTTP APIs, not a general web framework.
|
||||
**vl::inter_process::windows_http::HttpServer** derives from **vl::inter_process::windows_http::HttpServerApi** and implements **INetworkProtocolServer**. **vl::inter_process::windows_http::HttpClient** implements both **INetworkProtocolClient** and **INetworkProtocolConnection**. This transport is a raw protocol implementation over Windows HTTP APIs, not a general web framework.
|
||||
|
||||
The raw HTTP protocol uses these routes under the configured base URL:
|
||||
- **GET /VlppInterProcess/Connect** creates a logical connection and returns per-connection request and response URLs.
|
||||
- **POST /VlppInterProcess/Request/GUID** is the client-maintained long-poll request for server-to-client messages.
|
||||
- **POST /VlppInterProcess/Response/GUID** sends client-to-server messages and may also receive one queued server-to-client message.
|
||||
|
||||
**HttpClient::WaitForServer** sends the connect request, validates the returned URLs, records them and reports connection. **BeginReadingLoopUnsafe** starts the long-poll request loop. **SendString** posts to the response URL. Connect and response failures retry a limited number of times; request failures retry while the client is still running.
|
||||
**vl::inter_process::windows_http::HttpClient::WaitForServer** sends the connect request, validates the returned URLs, records them and reports connection. **BeginReadingLoopUnsafe** starts the long-poll request loop. **SendString** posts to the response URL. Connect and response failures retry a limited number of times; request failures retry while the client is still running.
|
||||
|
||||
**HttpServer** creates a **HttpServerConnection** for each connect request. Server-to-client messages are returned through a pending long-poll request when possible, or queued until the next request. Client-to-server request bodies are dispatched as inbound strings. When the server stops, pending long-poll requests are cancelled and connection callbacks receive disconnection.
|
||||
**vl::inter_process::windows_http::HttpServer** creates a **vl::inter_process::windows_http::HttpServerConnection** for each connect request. Server-to-client messages are returned through a pending long-poll request when possible, or queued until the next request. Client-to-server request bodies are dispatched as inbound strings. When the server stops, pending long-poll requests are cancelled and connection callbacks receive disconnection.
|
||||
|
||||
### Windows HTTP Helper APIs
|
||||
|
||||
**HttpClientApi** and **HttpServerApi** are lower-level Windows helpers used by **HttpClient** and **HttpServer**. Use them directly when a Windows feature needs asynchronous HTTP request and response behavior without adopting the raw **INetworkProtocol** transport shape.
|
||||
**vl::inter_process::windows_http::HttpClientApi** and **vl::inter_process::windows_http::HttpServerApi** are lower-level Windows helpers used by **vl::inter_process::windows_http::HttpClient** and **vl::inter_process::windows_http::HttpServer**. Use them directly when a Windows feature needs asynchronous HTTP request and response behavior without adopting the raw **INetworkProtocol** transport shape.
|
||||
|
||||
**HttpClientApi** owns one WinHTTP session and connection for a host and port. **HttpQuery** sends one asynchronous request described by **HttpRequest**, whose fields include method, query, body, content type, accept types, credentials, cookies, extra headers and timeouts. Results are either **HttpResponse** or **HttpError**; HTTP status codes such as 404 are represented as **HttpResponse** values.
|
||||
**vl::inter_process::windows_http::HttpClientApi** owns one WinHTTP session and connection for a host and port. **HttpQuery** sends one asynchronous request described by **vl::inter_process::windows_http::HttpRequest**, whose fields include method, query, body, content type, accept types, credentials, cookies, extra headers and timeouts. Results are either **vl::inter_process::windows_http::HttpResponse** or **vl::inter_process::windows_http::HttpError**; HTTP status codes such as 404 are represented as **HttpResponse** values.
|
||||
|
||||
**HttpServerApi** owns one HTTP.sys URL prefix. Override **OnHttpRequestReceived** to dispatch requests and **OnHttpServerStopping** to clean up feature state. Helpers such as **GetUtf8Body**, **SendResponse** and **SendResponseUtf8** handle common request-body and response tasks.
|
||||
**vl::inter_process::windows_http::HttpServerApi** owns one HTTP.sys URL prefix. Override **OnHttpRequestReceived** to dispatch requests and **OnHttpServerStopping** to clean up feature state. Helpers such as **GetUtf8Body**, **SendResponse** and **SendResponseUtf8** handle common request-body and response tasks.
|
||||
|
||||
|
||||
@@ -176,14 +176,14 @@ public:
|
||||
|
||||
The server channel accepts only clients that declare the RPC channel name. After the broker local client is connected, every accepted normal client is registered with RpcJsonDispatcherServer.
|
||||
```C++
|
||||
class JsonRpcChannelServer : public JsonNetworkChannelServer<NamedPipeServer>
|
||||
class JsonRpcChannelServer : public JsonNetworkChannelServer<named_pipe::NamedPipeServer>
|
||||
{
|
||||
private:
|
||||
RpcJsonDispatcherServer* dispatcher = nullptr;
|
||||
|
||||
public:
|
||||
JsonRpcChannelServer(Ptr<Parser> parser, const WString& pipeName)
|
||||
: JsonNetworkChannelServer<NamedPipeServer>(parser, pipeName)
|
||||
: JsonNetworkChannelServer<named_pipe::NamedPipeServer>(parser, pipeName)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -343,7 +343,7 @@ void HostLocalService(
|
||||
|
||||
## Connecting a Remote Client
|
||||
|
||||
A remote client uses JsonNetworkChannelClient over a raw transport such as NamedPipeClient or HttpClient. The channel connection supplies the client id; OnConnected initializes the RPC lifecycle for that id.
|
||||
A remote client uses JsonNetworkChannelClient over a raw transport such as **vl::inter_process::named_pipe::NamedPipeClient** or **vl::inter_process::windows_http::HttpClient**. The channel connection supplies the client id; OnConnected initializes the RPC lifecycle for that id.
|
||||
```C++
|
||||
class JsonRpcNetworkEndpoint : public JsonRpcNetworkClient
|
||||
{
|
||||
@@ -373,7 +373,7 @@ Ptr<MyRpcDispatcherClient> ConnectRemoteRpcClient(
|
||||
const List<WString>& waitingForServices)
|
||||
{
|
||||
auto dispatcher = Ptr(new MyRpcDispatcherClient(taskQueue));
|
||||
auto transport = Ptr(new NamedPipeClient(L"WorkflowRpcPipe"));
|
||||
auto transport = Ptr(new named_pipe::NamedPipeClient(L"WorkflowRpcPipe"));
|
||||
auto channelClient = Ptr(new JsonRpcNetworkEndpoint(dispatcher, transport, parser));
|
||||
|
||||
dispatcher->WaitForServer(
|
||||
|
||||
Reference in New Issue
Block a user