using System; using System.Runtime.InteropServices; namespace S3.Modules.Popout { // Must match InputEvent in native/include/shared_types.h exactly. [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct InputEvent { public int type; // InputEventType (0=Move,1=LDown,2=LUp,3=RDown,4=RUp,5=Wheel) public float x; // normalized [0,1] left=0 public float y; // normalized [0,1] top=0 public float delta; // wheel delta; positive = scroll up } public enum InputEventType { MouseMove = 0, LButtonDown = 1, LButtonUp = 2, RButtonDown = 3, RButtonUp = 4, MouseWheel = 5, UICommand = 6, // toolbar button; x = UICmd value cast to float } public enum UICmd { Recenter = 1, FollowMode = 2, // toggle MapEnhancer follow on/off FollowPlayerCam = 3, // follow Camera.main continuously FollowSelectedLoco = 4, // follow TrainController.Shared.SelectedCar FollowLoco = 5, // follow specific loco; InputEvent.y = 0-based index JumpToLocation = 6, // jump to named location; InputEvent.y = 0-based index SetRotation = 7, // set map rotation; InputEvent.y = degrees [0,360) RotateReset = 8, // reset map rotation to 0 RotateSyncPlayer = 9, // toggle sync-rotation-to-player-camera mode ToggleFollowPlayer = 10, // toggle continuous follow of player world position PopOut = 11, // (in-game only) close the overlay and open the OS popout SetTheme = 12, // (in-game only) apply theme preset; InputEvent.y = preset index SetAlpha = 13, // (in-game only) set chrome alpha; InputEvent.y = [0.1, 1.0] Close = 14, // (in-game only) user clicked [X] — hide the overlay SetMapAlpha = 15, // (in-game only) set map image alpha; InputEvent.y = [0.0, 1.0] // MapEnhancer settings SetMEBool = 16, // toggle ME bool setting; y = MEBoolBit index, delta = 0|1 SetMEFloat = 17, // set ME float setting; y = MEFloatIdx index, delta = value MEResetSwitchesNormal = 18, // bulk reset all switches to Normal MEResetSwitchesThrown = 19, // bulk reset all switches to Thrown TogglePanDisablesFollow = 20, // toggle whether panning cancels follow mode ToggleRightClickRecenter = 21, // toggle whether right-clicking recenters on player SetMapBgAlpha = 22, // set map camera clear-colour opacity; y = [0.0, 1.0] } // Must match MapThemeData in native/include/shared_types.h exactly (36 floats = 144 bytes). [Serializable] [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct MapThemeData { public float wBgR, wBgG, wBgB, wBgA; // window / toolbar background public float accR, accG, accB, accA; // accent (TitleBgActive, buttons) public float txtR, txtG, txtB, txtA; // status text public float popR, popG, popB, popA; // settings popup background public float mapR, mapG, mapB, mapA; // map image tint (1,1,1,1 = no tint) public float cmpR, cmpG, cmpB, cmpA; // compass background disk public float nNR, nNG, nNB, nNA; // compass needle N half public float nSR, nSG, nSB, nSA; // compass needle S half public float mapBgR, mapBgG, mapBgB, mapBgA; // map camera clear colour } internal static class Native { // S3Native.dll lives in the mod folder (Mods/S3) and is loaded by full path // via NativeLoader before any of these are called; once loaded, [DllImport] // binds to it by name. (Pure-UMM install — no game-root copy, no winhttp proxy.) // Named S3Native (not RRPopout) so it can never collide with a leftover copy // of the old standalone PopOut's RRPopout.dll still injected in someone's game. private const string Dll = "S3Native"; // Create a floating window. Returns a handle (> 0) or 0 on failure. // Native loads saved position/size from its state file; width/height are the first-run defaults. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern int RRPOPOUT_CreateWindow(string title, int width, int height); // Set the source texture and the UV sub-rect to blit next frame. // Call this on the main thread immediately before IssuePluginEvent. // u0,v0 = top-left UV in D3D convention (V=0 at top); u1,v1 = bottom-right. // Pass (0, 0, 1, 1) for full texture. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_SetFrameTexture( int windowHandle, IntPtr texturePtr, float u0, float v0, float u1, float v1); // Returns the UnityRenderingEvent callback pointer for GL.IssuePluginEvent. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr RRPOPOUT_GetRenderEventFunc(); // Fills outWidth / outHeight with the current client area size. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_GetWindowSize(int windowHandle, out int outWidth, out int outHeight); // Drains queued input events from the OS window into outEvents. // Returns the number of events written (≤ maxEvents). [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int RRPOPOUT_PollInputEvents( int windowHandle, [Out] InputEvent[] outEvents, int maxEvents); // Close and free the window. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_DestroyWindow(int windowHandle); // Update the status bar text shown at the bottom of the popout. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern void RRPOPOUT_SetStatusText(int windowHandle, string text); // Set the locomotive list shown in the Follow submenu. // names: loco display names joined by '\n'. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern void RRPOPOUT_SetLocoList(int windowHandle, string names); // Set the location list shown in the Jump to location submenu. // names: location names joined by '\n'. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern void RRPOPOUT_SetLocationList(int windowHandle, string names); // Tell the native UI whether MapEnhancer is installed (controls menu layout). [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_SetMapEnhancerInstalled(int windowHandle, bool installed); // Update the compass display to reflect the current map rotation (degrees). [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_SetMapRotation(int windowHandle, float degrees); // Highlight (or un-highlight) the sync button in the toolbar. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_SetMapSyncPlayer(int windowHandle, bool active); // Highlight (or un-highlight) the follow-player toolbar button. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_SetFollowPlayer(int windowHandle, bool active); // Push current map zoom so native can persist it on window close. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_SetMapZoom(int windowHandle, float zoom); // Read back the C#-side state that native loaded from its save file. // followPlayer / syncRotation return 1 (true) or 0 (false). [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_GetPersistedState(int windowHandle, out int followPlayer, out int syncRotation, out float mapRotation, out float mapZoom); // ------------------------------------------------------------------- // In-game overlay. Shares the native engine's shared ImGui context // and D3D11 device; renders into Unity's bound backbuffer after frame. // ------------------------------------------------------------------- // Event id to pass to GL.IssuePluginEvent to drive the overlay render. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int RRPOPOUT_GetOverlayEventId(); // One-time: hand native a texture so it can acquire Unity's D3D device. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_SetOverlayDeviceTexture(IntPtr texturePtr); // Per-frame input snapshot (top-left origin, pixels; wheel in WHEEL_DELTA units). [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_SetOverlayInput( float displayW, float displayH, float mouseX, float mouseY, int lButton, int rButton, int wheel); // Show or hide the overlay. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_SetOverlayVisible(int visible); // Map render texture to display inside the in-game overlay window, plus the // UV sub-rect (V flipped for a Unity RT: v0=1, v1=0). IntPtr.Zero clears it. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_SetOverlayMapTexture( IntPtr texturePtr, float u0, float v0, float u1, float v1); // 1 when ImGui wants the mouse this frame (hovering a widget). [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int RRPOPOUT_OverlayWantsMouse(); // Drains queued in-game map input (drag/zoom over the map image). Events are // in normalized [0,1] image space (top-left origin); forward to the map camera. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int RRPOPOUT_PollOverlayInput( [Out] InputEvent[] outEvents, int maxEvents); // Reads the map image region size (px) so C# can set camera.aspect to match, // filling the window with no letterbox bars. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_GetOverlayMapView(out float outW, out float outH); // Handle of the in-game overlay's shared UI-state holder. Push state to it // (status/loco/location/rotation/follow) and poll it for toolbar commands // with the same handle-based functions the popout uses. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int RRPOPOUT_GetOverlayWindowHandle(); // Apply a theme to the shared ImGui context. Affects both the in-game overlay // and the popout (they share one context). presetIndex is stored for the gear // menu checkmarks. Thread-safe: stored on main thread, applied on render thread. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_SetTheme(ref MapThemeData data, int presetIndex); // Set the in-game overlay's chrome alpha [0.1, 1.0]. Thread-safe via atomic. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_SetOverlayAlpha(float alpha); // Set the map image alpha [0.0, 1.0]. Independent of chrome alpha. [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_SetOverlayMapAlpha(float alpha); // Tell native whether panning should cancel follow mode (for the gear menu checkmark). [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_SetPanDisablesFollow(int windowHandle, bool active); // Tell native whether right-clicking the map recenters on the player (gear menu checkmark). [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_SetRightClickRecenter(int windowHandle, bool active); // Seed the map camera clear-colour opacity slider (shared global, overlay + popout). [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_SetOverlayMapBgAlpha(float alpha); // Push MapEnhancer settings state to a window's render-thread atomics. // flags: bit-packed booleans (see MEBoolBit in shared_types.h). // Scale values seed slider positions; sliders update them live during drag // and push UICmd::SetMEFloat only on release (IsItemDeactivatedAfterEdit). [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RRPOPOUT_SetMEState( int windowHandle, uint flags, float flareScale, float junctionScale, float trackThickness, float crossingScale); } }