using System;
using System.Collections.Generic;
using System.Runtime.InteropServices.JavaScript;
namespace Examples.Web;
///
/// Browser host: owns the single raylib window and the "current" example. The frame loop is
/// driven from JavaScript (main.js, via requestAnimationFrame) since a blocking C# loop would
/// freeze the page. Methods marked [JSExport] are called from main.js.
///
///
/// Every switch applies and resets the cursor to visible.
/// Cursor hiding, , and per-example window size are not
/// honored in the browser; the canvas stays 800x450 and the pointer stays visible.
///
///
public partial class Host
{
public const int screenWidth = 800;
public const int screenHeight = 450;
private static readonly List _examples = new(ExampleRegistry.BrowserExamples);
private static IExample _current;
public static void Main()
{
InitWindow(screenWidth, screenHeight, "raylib-cs web examples");
SetTargetFPS(60);
// main.js selects the first example, so its init failures surface through SetExample.
}
/// Render one frame of the current example; false if it threw and was torn down.
[JSExport]
public static bool UpdateFrame()
{
if (_current == null)
{
return true;
}
try
{
_current.Update();
return true;
}
catch (Exception ex)
{
// Don't let one misbehaving example kill the whole page; log and stop driving it.
Console.WriteLine($"[Examples.Web] '{_current.Name}' threw during Update: {ex}");
TryUnload(_current);
_current = null;
return false;
}
}
/// Switch the active example by name; false on unknown name or failed Init.
[JSExport]
public static bool SetExample(string name)
{
var next = _examples.Find(e => e.Name == name);
if (next == null)
{
return false;
}
if (_current != null)
{
TryUnload(_current);
_current = null;
}
// Reset the baseline; an example may hide/lock the cursor from its own Update(), and
// CursorDisabled/CursorHidden are deliberately never applied in the browser.
EnableCursor();
ShowCursor();
SetTargetFPS(next.TargetFps);
try
{
next.Init();
_current = next;
return true;
}
catch (Exception ex)
{
Console.WriteLine($"[Examples.Web] '{next.Name}' failed to initialize: {ex}");
TryUnload(next);
return false;
}
}
/// Target FPS of the current example; main.js paces the frame loop with it.
[JSExport]
public static int GetCurrentTargetFps()
{
return _current?.TargetFps ?? 60;
}
/// Newline-separated example names, used to populate the nav dropdown.
[JSExport]
public static string GetExampleNames()
{
return string.Join("\n", _examples.ConvertAll(e => e.Name));
}
///
/// Map browser CSS mouse coordinates to the fixed 800x450 framebuffer when the canvas
/// is CSS-scaled (integer/fit/native display modes in main.js).
///
[JSExport]
public static void SetMouseScaleFromDisplay(float scaleX, float scaleY)
{
SetMouseOffset(0, 0);
SetMouseScale(scaleX, scaleY);
}
/// Best-effort Unload that never lets a failure cascade.
private static void TryUnload(IExample example)
{
try
{
example.Unload();
}
catch (Exception ex)
{
Console.WriteLine($"[Examples.Web] '{example.Name}' threw during Unload: {ex}");
}
}
}