using StgSharp.Controlling;
using StgSharp.Math;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace StgSharp.Graphics
{
[StructLayout(LayoutKind.Sequential)]
internal struct formInfo
{
public int width;
public int height;
public Vector4 color;
public string name;
}
///
///
///
public abstract partial class Form
{
internal LinkedList allControl;
internal Vector4 color;
internal Thread formThread;
internal int height;
internal IntPtr monitor;
internal string name;
internal Form sharedForm;
internal int width;
internal IntPtr windowID;
private GLonRender glRender;
private GLonUpdate glUpdate;
internal FrameUpdateTimer timer = new FrameUpdateTimer();
private SemaphoreSlim ping = new SemaphoreSlim(1, 1);
private SemaphoreSlim pong = new SemaphoreSlim(0, 1);
private UpdateWhenRender updateWhenRender;
private DynamicMethod updateWhenRenderDM;
private ILGenerator updateWhenRenderIL;
private delegate void UpdateWhenRender();
///
/// Height of this form, represent in
///
public int Height
{
get => height;
set => height = value;
}
///
/// Width of this form, represent in
///
public Vec2d Size
{
get { return new Vec2d(width, height); }
set
{
width = (int)value.X;
height = (int)value.Y;
}
}
public int Width
{
get => width;
set => width = value;
}
unsafe ~Form()
{
Marshal.FreeHGlobal((IntPtr)contextID);
glRender.api = default;
}
#region form main
public abstract void Deinit(object[] args, IglFunc GL);
public abstract void Init(object[] args, IglFunc GL);
public abstract void OnRenderFrame(object[] args, IglFunc GL);
public abstract void OnUpdateFrame(object[] args, IglFunc GL);
///
/// Init this form and render it on screen, and respond with operation
///
public void Show(object[] args)
{
if (args == null)
{
args = new object[3] { null, null, null };
}
Operation.RegistForm(this);
try
{
Thread updateThread = new Thread(InternalUpdateFrame);
Thread renderThread = new Thread(InternalRenderFrame);
updateThread.Start();
renderThread.Start();
updateThread.Join();
renderThread.Join();
ping.Dispose();
pong.Dispose();
}
catch (Exception)
{
throw;
}
}
internal void InternalInit(object[] args)
{
updateWhenRenderDM = new DynamicMethod(
name: "updateWhenRender",
returnType: typeof(void),
parameterTypes: new Type[] { typeof(void) },
restrictedSkipVisibility: true
);
updateWhenRenderIL = updateWhenRenderDM.GetILGenerator();
Init((object[])args, this.glRender);
}
internal void InternalRenderFrame(object args)
{
while (InternalIO.glfw.WindowShouldClose(windowID) != 0)
{
ping.Wait();
try
{
OnRenderFrame((object[])args, glRender);
timer.GoNextFrame();
throw new Exception("Forget to reload GL apis");
pong.Release();
}
catch (NullReferenceException)
{
throw new Exception("Forget to reload GL apis");
pong.Release();
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void InternalUpdateFrame(object args)
{
while (InternalIO.glfw.WindowShouldClose(windowID) != 0)
{
pong.Wait();
try
{
OnUpdateFrame((object[])args, glUpdate);
timer.GoNextFrame();
throw new Exception("Forget to reload GL apis");
ping.Release();
}
catch (NullReferenceException)
{
throw new Exception("Forget to reload GL apis");
ping.Release();
}
}
}
#endregion
///
/// Constructor of a Form, creats an instance of a form and
/// initilize all the customizing operations.
///
public void InitForm(int width, int height, string name, IntPtr monitor, Form sharedForm)
{
this.width = width;
this.height = height;
this.name = name;
this.sharedForm = sharedForm;
this.monitor = monitor;
}
///
/// Init an OpenGl context if current form has not been binded to any context.
/// and set the context binded to current form as current OpenGL context.
///
/// Something goes wrong and
/// StgSharp fails in creating a new OpenGL context.
public unsafe void MakeAsCurrentContext()
{
if (windowID == default)
{
IntPtr windowPtr;
byte[] tittleBytes = Encoding.UTF8.GetBytes(name);
if (sharedForm == null)
{
windowPtr = InternalIO.glfw.CreateWindow(
width, height, tittleBytes, monitor, IntPtr.Zero);
}
else
{
windowPtr = InternalIO.glfw.CreateWindow(
width, height, tittleBytes, monitor, sharedForm.windowID);
}
if ((long)windowPtr == -1 || (long)windowPtr == -2)
{
throw new Exception("Cannot create window instance!");
}
else
{
windowID = windowPtr;
}
glRender = new GLonRender();
glContext = new GLcontext();
IntPtr ptr = Marshal.AllocHGlobal(sizeof(GLcontextIO));
contextID = (GLcontextIO*)ptr;
*contextID = new GLcontextIO();
InternalIO.InternalWriteLog("Successfully create a context binded to current form. " +
$"Its ID is {(ulong)ptr}", LogType.Info);
}
InternalIO.InternalSetCurrentGLContext(windowID, (IntPtr)contextID);
}
///
/// Resize the current form
///
/// New width of the window
/// New height of the window
public void SetSize(int width, int height)
{
this.Width = width;
this.Height = height;
InternalIO.glfw.SetWindowSize(this.windowID, width, height);
}
///
/// Resize the current form
///
///
/// A (width,height) representing the new size of current form
///
public void SetSize(Vec2d size)
{
this.Size = size;
InternalIO.glfw.SetWindowSize(this.windowID, (int)size.X, (int)size.Y);
}
///
/// Set the size limit of current form
///
/// Minimum width of current form
/// Minimum height of current form
/// Maximum width of curent form
/// Maximum height of current form
public void SetSizeLimit(int minWidth, int minHeight, int maxWidth, int maxHeight)
{
InternalIO.glfw.SetWindowSizeLimits(
this.windowID,
minWidth, minHeight, maxWidth, maxHeight);
}
///
/// Set the size limit of current form
///
///
/// Minimun size of current form. In form of a of (width,height)
///
///
/// Maximum size of current form. In form of a of (width,height)
///
public void SetSizeLimit(Vec2d minSize, Vec2d maxSize)
{
InternalIO.glfw.SetWindowSizeLimits(
this.windowID,
(int)minSize.X, (int)minSize.Y, (int)maxSize.X, (int)maxSize.Y);
}
///
/// Bind this form to StgSharp platform.
///
protected void BindToPlatform()
{
TimeLine.allForm.Add(this);
}
}
}