#pragma once #define WIN32_LEAN_AND_MEAN #include #include #include #include #include #include #include #include "input_queue.h" struct PopoutWindow { HWND hwnd = nullptr; HWND contentHwnd = nullptr; // D3D11 swapchain target IDXGISwapChain* swapChain = nullptr; ID3D11RenderTargetView* rtv = nullptr; // Message-pump thread that owns hwnd. Kept open for the window's lifetime so // DestroyPopoutWindow can join it before freeing this struct (the pump thread // touches `this` while processing WM_DESTROY). HANDLE pumpThread = nullptr; // Swapchain dimensions — content area only (no status bar gap any more; // the ImGui toolbar is an overlay rendered into the same swapchain). std::atomic width {800}; std::atomic height {600}; // Texture + UV rect written by RRPOPOUT_SetFrameTexture on the main thread, // read by Renderer_Present on the render thread. std::atomic pendingTexture {nullptr}; std::atomic srcU0 {0.f}, srcV0 {0.f}; std::atomic srcU1 {1.f}, srcV1 {1.f}; std::atomic resizing {false}; std::atomic destroyRequested {false}; std::atomic alive {true}; InputQueue inputQueue; // ----------------------------------------------------------------------- // ImGui input state // Written by the Win32 message pump thread; consumed by the render thread. // ----------------------------------------------------------------------- std::atomic imMouseX {-1.f}; std::atomic imMouseY {-1.f}; std::atomic imLButton {false}; std::atomic imRButton {false}; // Wheel accumulator in raw WHEEL_DELTA units (120 per notch). // fetch_add on message pump; exchange(0) on render thread. std::atomic imWheelRaw {0}; // Written by render thread after each ImGui frame. // When true, PollInputEvents suppresses mouse events so they don't reach Unity. std::atomic imWantMouse {false}; // When true, Present blits the frame texture only: no map toolbar, compass, // radio rail, or view presets. Used by Car Cards (and any future non-map window). std::atomic imPlainContent {false}; // ----------------------------------------------------------------------- // Status bar label (UTF-8), shown in the ImGui toolbar. // Written by RRPOPOUT_SetStatusText (main thread), read by render thread. // ----------------------------------------------------------------------- char imStatusText[256]; std::mutex imStatusMutex; // ----------------------------------------------------------------------- // Dynamic menu lists (UTF-8 labels), set by RRPOPOUT_SetLocoList / // RRPOPOUT_SetLocationList from the C# side every few seconds. // Read by the render thread inside the ImGui settings menu. // ----------------------------------------------------------------------- struct ImMenuItem { char label[128]; }; std::vector imLocoList; std::mutex imLocoMutex; std::vector imLocationList; std::mutex imLocationMutex; // ----------------------------------------------------------------------- // Map rotation + ME flag (C# → render thread via exports) // ----------------------------------------------------------------------- std::atomic imMapRotationDeg {0.f}; std::atomic imMapZoom {500.f}; std::atomic imMapSyncPlayer {false}; std::atomic imMapEnhancerInstalled {false}; std::atomic imMEHasTurntable {false}; // true if ShowTurntableMarkers exists (v1.6.0 or community) std::atomic imMECommunity {false}; // true = community build (has crossing/spawn/switch-reset) std::atomic imFollowPlayer {false}; std::atomic imAlwaysOnTop {false}; std::atomic imPanDisablesFollow {true}; std::atomic imRightClickRecenter {true}; // ----------------------------------------------------------------------- // Icon culling + EOTD settings (C# → render thread via RRPOPOUT_SetIconCullingState) // ----------------------------------------------------------------------- std::atomic imIconCullingEnabled {true}; std::atomic imCullThreshold {0.40f}; std::atomic imHideMuLocos {true}; std::atomic imLocoBoostedScale {1.5f}; std::atomic imEotdEnabled {true}; std::atomic imEotdOnlyWhenCulled {true}; std::atomic imEotdSizeScale {8.0f}; // ----------------------------------------------------------------------- // MapEnhancer settings state (C# → render thread via RRPOPOUT_SetMEState) // Bit layout of imMEFlags matches MEBoolBit enum in shared_types.h. // Scale atomics are live-updated during slider drag so the slider doesn't // snap back between frames, then the final value is pushed as UICmd::SetMEFloat. // ----------------------------------------------------------------------- std::atomic imMEFlags {0}; std::atomic imMEFlareScale {0.6f}; std::atomic imMEJunctionSc {0.6f}; std::atomic imMETrackThick {1.25f}; std::atomic imMECrossingSc {0.3f}; // ----------------------------------------------------------------------- // Track name labels (industry track names + viewport UVs, pushed from C# each frame). // C# projects TrackSpan centroids through the map camera, culls off-screen entries, // and pushes (name, centroid_uv, anchors). Native runs per-frame collision avoidance // (AABB push in pixel space) then draws leader lines to each anchor before the text. // ----------------------------------------------------------------------- struct ImTrackLabel { char name[64]; float u, v; // centroid UV — starting position for the layout solver float angle; // track direction in screen space (degrees; 0=right, +ve=CW) float scale; // font size multiplier (1.0 = track label, 1.5 = industry label) int anchorStart; // index into imAnchorUs/Vs int anchorCount; // 1 for single span; >1 for merged same-name cluster }; std::vector imTrackLabels; std::vector imAnchorUs; // flat anchor UV arrays, indexed by anchorStart std::vector imAnchorVs; std::mutex imTrackLabelMutex; std::atomic imTrackLabelsEnabled {true}; std::atomic imTrackLabelFontSize {14.f}; std::atomic imTrackLabelLineThickness {1.5f}; std::atomic imTrackLabelZoomLimit {3000.f}; std::atomic imTrackLeaderLinesEnabled {true}; std::atomic imTrackCollisionEnabled {true}; std::atomic imTrackParallelEnabled {false}; std::atomic imTrackMergeEnabled {true}; std::atomic imTrackLabelMergeZoom {150.f}; std::atomic imTrackIndustryZoom {200.f}; std::atomic imTrackUtilityRepair {true}; std::atomic imTrackUtilityDiesel {true}; std::atomic imTrackUtilityLoader {true}; std::atomic imTrackUtilityInterchange {true}; std::atomic imTrackUtilityZoom {200.f}; std::atomic imTrackAllLabelsZoom {8000.f}; std::atomic imTrackLabelAvoidTrack {false}; std::atomic imTrackLabelFontSizeMin {8.f}; // Named camera-view presets (C# owns the data; native draws the left rail). std::vector imPresetList; std::mutex imPresetMutex; std::atomic imPresetEditIndex {-1}; std::atomic imPresetPreviewing {false}; std::atomic imPresetPendingDelete{-1}; char imPresetRenameBuf[128] {}; // AE waypoint pins (gear-menu toggles). std::atomic imWaypointsEnabled {true}; std::atomic imWaypointsSelectedOnly {false}; // Radio-control rail (top-right). C# owns pin ids; native draws the list. std::vector imRadioList; std::vector imRadioColors; std::mutex imRadioMutex; std::atomic imRadioOn {false}; std::atomic imRadioSelected {-1}; std::atomic imRadioTool {0}; std::atomic imRadioWq {false}; std::atomic imRadioAeBits {0}; std::atomic imRadioForward {true}; std::atomic imRadioSpeed {15.f}; std::atomic imRadioEditIndex {-1}; char imRadioRenameBuf[128] {}; // Waypoint-mode ghost arrow (Unity viewport UV, v=0 at bottom) + near-cursor popup. std::atomic imRadioGhostOn {false}; std::atomic imRadioGhostU {0.f}; std::atomic imRadioGhostV {0.f}; std::atomic imRadioGhostAngle {0.f}; // screen deg, 0=right, +CW, y-down std::atomic imRadioGhostColor {0xFFFFFFFFu}; std::atomic imRadioWpStage {0}; // 0 off, 1 choose order, 2 enter count std::atomic imRadioWpU {0.f}; std::atomic imRadioWpV {0.f}; std::atomic imRadioWpFlags {0}; // bit0 = has couple target std::atomic imRadioWpCount {1}; // Keyboard for ImGui InputText (preset rename). Pump thread writes, render thread reads. std::mutex imKeyMutex; char imCharsUtf8[512] {}; std::atomic imKeyDown {0}; std::atomic imKeyMods {0}; uint32_t imPrevKeyDown = 0; uint32_t imPrevKeyMods = 0; // Loaded geometry from the save file — consumed by MessagePumpThread before CreateWindowExW. std::atomic lastWinX {-1}, lastWinY {-1}; std::atomic lastWinW {900}, lastWinH {700}; PopoutWindow() { // Status starts empty (filled live with zoom/coords). Avoids a default label // that would be redundant with the window title bar and would render any // non-ASCII glyph as '?' in ImGui's default font. imStatusText[0] = '\0'; } };