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 } 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(); } }