Adapting InputManager to SDL

This commit is contained in:
Ale32bit 2023-09-02 10:12:24 +02:00
parent 0d358b09b9
commit 110afd1ca4
26 changed files with 579 additions and 495 deletions

View file

@ -1,4 +1,4 @@
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
// Copyright 2023 Alessandro "AlexDevs" Proto
//
// Licensed under the Apache License, Version 2.0 (the "License").
@ -13,37 +13,56 @@
// See the License for the specific language governing permissions and
// limitations under the License.
using Capy64.API;
using Capy64.Core;
using Capy64.Eventing;
using Capy64.Extensions;
using Capy64.Integrations;
using Capy64.PluginManager;
using Capy64.Runtime;
using Microsoft.Extensions.Configuration;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using MonoGame.Extended;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using static Capy64.Utils;
namespace Capy64;
public enum EngineMode
{
Classic,
Free
}
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using static Utils;
using static SDL2.SDL;
public class Capy64 : Game
public class Capy64 : IDisposable
{
public const string Version = "1.1.0-beta";
public nint Window { get; private set; } = 0;
public nint Renderer { get; private set; } = 0;
public nint VideoSurface { get; private set; } = 0;
public nint SurfaceRenderer { get; private set; } = 0;
public int WindowWidth { get; private set; }
public int WindowHeight { get; private set; }
public int Width { get; set; } = DefaultParameters.Width;
public int Height { get; set; } = DefaultParameters.Height;
public float Scale { get; set; } = DefaultParameters.Scale;
public int Tickrate { get; set; } = DefaultParameters.ClassicTickrate;
public int Framerate { get; set; } = 60;
public float Ticktime => 1000f / Tickrate;
public float Frametime => 1000f / Framerate;
public Color BorderColor { get; set; } = Color.Blue;
public Borders Borders {
get => _borders;
set {
_borders = value;
_outputRect = new() {
x = value.Left,
y = value.Top,
w = (int)(Width * Scale),
h = (int)(Height * Scale),
};
}
}
private SDL_Rect _outputRect;
private Borders _borders;
public static readonly string AssemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
public static readonly string AssetsPath = Path.Combine(AssemblyPath, "Assets");
public static class DefaultParameters
{
@ -57,165 +76,41 @@ public class Capy64 : Game
public const int FreeTickrate = 60;
}
public static readonly string AssemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
public static readonly string AssetsPath = Path.Combine(AssemblyPath, "Assets");
public static string AppDataPath
{
get
{
public static string AppDataPath {
get {
string baseDir =
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData,
Environment.SpecialFolderOption.Create) :
RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ?
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData,
Environment.SpecialFolderOption.Create) :
RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ?
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData,
Environment.SpecialFolderOption.Create) :
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData,
Environment.SpecialFolderOption.Create) :
RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? Environment.GetFolderPath(
Environment.SpecialFolder.LocalApplicationData,
Environment.SpecialFolderOption.Create) :
RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? Environment.GetFolderPath(
Environment.SpecialFolder.LocalApplicationData,
Environment.SpecialFolderOption.Create) :
"./";
return Path.Combine(baseDir, "Capy64");
}
}
public static Capy64 Instance { get; private set; }
public Capy64 Game => this;
public EngineMode EngineMode { get; private set; } = EngineMode.Classic;
public IList<IComponent> NativePlugins { get; private set; }
public IList<IComponent> Plugins { get; private set; }
public int Width { get; set; } = DefaultParameters.Width;
public int Height { get; set; } = DefaultParameters.Height;
public float Scale { get; set; } = DefaultParameters.Scale;
public Drawing Drawing { get; private set; }
public Audio Audio { get; private set; }
public LuaState LuaRuntime { get; set; }
public Eventing.EventEmitter EventEmitter { get; private set; }
public DiscordIntegration Discord { get; set; }
public int TickRate => tickrate;
public IConfiguration Configuration { get; private set; }
public Color BorderColor { get; set; } = Color.Black;
public Borders Borders = new()
{
Top = 0,
Bottom = 0,
Left = 0,
Right = 0,
};
public SpriteBatch SpriteBatch;
private readonly InputManager _inputManager;
private RenderTarget2D renderTarget;
private readonly GraphicsDeviceManager _graphics;
private ulong _totalTicks = 0;
private int tickrate = 0;
private int everyTick => 60 / tickrate;
private bool _running = false;
private InputManager _inputManager;
public EventEmitter EventEmitter = new();
public Capy64()
{
Instance = this;
ResetBorder();
WindowWidth = (int)(Width * Scale) + Borders.Left + Borders.Right;
WindowHeight = (int)(Height * Scale) + Borders.Top + Borders.Bottom;
_graphics = new GraphicsDeviceManager(this);
//Content.RootDirectory = "Content";
IsMouseVisible = true;
EventEmitter = new();
_inputManager = new(this, EventEmitter);
Drawing = new();
}
public void SetEngineMode(EngineMode mode)
{
switch (mode)
{
case EngineMode.Classic:
tickrate = DefaultParameters.ClassicTickrate;
Width = DefaultParameters.Width;
Height = DefaultParameters.Height;
Window.AllowUserResizing = false;
ResetBorder();
break;
case EngineMode.Free:
tickrate = DefaultParameters.FreeTickrate;
Window.AllowUserResizing = true;
break;
}
EngineMode = mode;
UpdateSize(true);
}
public void UpdateSize(bool resize = true)
{
if (resize)
{
_graphics.PreferredBackBufferWidth = (int)(Width * Scale) + Borders.Left + Borders.Right;
_graphics.PreferredBackBufferHeight = (int)(Height * Scale) + Borders.Top + Borders.Bottom;
_graphics.ApplyChanges();
}
renderTarget = new RenderTarget2D(
GraphicsDevice,
Width,
Height,
false,
GraphicsDevice.PresentationParameters.BackBufferFormat,
DepthFormat.Depth24, 0, RenderTargetUsage.PreserveContents);
Drawing.Canvas = renderTarget;
_inputManager.Texture = renderTarget;
EventEmitter.RaiseScreenSizeChange();
}
private void OnWindowSizeChange(object sender, EventArgs e)
{
if (EngineMode == EngineMode.Classic)
{
UpdateSize(true);
return;
}
var bounds = Window.ClientBounds;
Width = (int)(bounds.Width / Scale);
Height = (int)(bounds.Height / Scale);
if (Window.IsMaximized())
{
var vertical = bounds.Height - (Height * Scale);
var horizontal = bounds.Width - (Width * Scale);
Borders.Top = (int)Math.Floor(vertical / 2d);
Borders.Bottom = (int)Math.Ceiling(vertical / 2d);
Borders.Left = (int)Math.Floor(horizontal / 2d);
Borders.Right = (int)Math.Ceiling(horizontal / 2d);
UpdateSize(false);
}
else
{
ResetBorder();
UpdateSize();
}
_inputManager = new(null, EventEmitter);
}
private void ResetBorder()
{
var size = (int)(Scale * DefaultParameters.BorderMultiplier);
Borders = new Borders
{
Borders = new Borders {
Top = size,
Bottom = size,
Left = size,
@ -223,116 +118,113 @@ public class Capy64 : Game
};
}
protected override void Initialize()
public void Run()
{
var configBuilder = new ConfigurationBuilder();
#if DEBUG
SDL_SetHint(SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING, "1");
#endif
var settingsPath = Path.Combine(AppDataPath, "settings.json");
if (!Directory.Exists(AppDataPath))
if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
{
Directory.CreateDirectory(AppDataPath);
}
if (!File.Exists(settingsPath))
{
File.Copy(Path.Combine(AssetsPath, "default.json"), settingsPath);
Console.WriteLine(SDL_GetError());
return;
}
configBuilder.AddJsonFile(Path.Combine(AssetsPath, "default.json"), false);
configBuilder.AddJsonFile(settingsPath, false);
Window = SDL_CreateWindow("Capy64 " + Version,
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
WindowWidth, WindowHeight,
SDL_WindowFlags.SDL_WINDOW_OPENGL);
Configuration = configBuilder.Build();
Window.Title = "Capy64 " + Version;
Scale = Configuration.GetValue("Window:Scale", DefaultParameters.Scale);
ResetBorder();
UpdateSize();
Window.AllowUserResizing = true;
Window.ClientSizeChanged += OnWindowSizeChange;
InactiveSleepTime = new TimeSpan(0);
SetEngineMode(Configuration.GetValue<EngineMode>("EngineMode", DefaultParameters.EngineMode));
Audio = new Audio();
NativePlugins = GetNativePlugins();
var safeMode = Configuration.GetValue("SafeMode", false);
if (!safeMode)
Plugins = PluginLoader.LoadAllPlugins(Path.Combine(AppDataPath, "plugins"));
EventEmitter.RaiseInit();
base.Initialize();
}
private List<IComponent> GetNativePlugins()
{
var iType = typeof(IComponent);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => iType.IsAssignableFrom(p) && !p.IsInterface);
var plugins = new List<IComponent>();
foreach (var type in types)
if (Window == nint.Zero)
{
var instance = (IComponent)Activator.CreateInstance(type, this);
plugins.Add(instance);
Console.WriteLine(SDL_GetError());
return;
}
return plugins;
}
var icon = SDL_LoadBMP("./Icon.bmp");
if(icon != 0)
SDL_SetWindowIcon(Window, icon);
Renderer = SDL_CreateRenderer(Window,
0,
SDL_RendererFlags.SDL_RENDERER_ACCELERATED | SDL_RendererFlags.SDL_RENDERER_PRESENTVSYNC);
protected override void LoadContent()
{
SpriteBatch = new SpriteBatch(GraphicsDevice);
}
protected override void Update(GameTime gameTime)
{
Drawing.Begin();
// Register user input
_inputManager.Update(IsActive);
EventEmitter.RaiseTick(new()
if (Renderer == nint.Zero)
{
GameTime = gameTime,
TotalTicks = _totalTicks,
IsActiveTick = (int)_totalTicks % everyTick == 0,
});
Console.WriteLine(SDL_GetError());
return;
}
Drawing.End();
VideoSurface = SDL_CreateRGBSurfaceWithFormat(0, Width, Height, 32, SDL_PIXELFORMAT_ARGB8888);
SurfaceRenderer = SDL_CreateSoftwareRenderer(VideoSurface);
_totalTicks++;
SDL_SetRenderDrawColor(SurfaceRenderer, 255, 255, 255, 255);
SDL_RenderClear(SurfaceRenderer);
base.Update(gameTime);
_running = true;
ulong deltaEnd = 0;
var perfFreq = SDL_GetPerformanceFrequency();
while (_running)
{
var deltaStart = SDL_GetPerformanceCounter();
var delta = deltaStart - deltaEnd;
while (SDL_PollEvent(out var ev) != 0)
{
ProcessEvent(ev);
}
if ((uint)(perfFreq / delta) >= Framerate)
{
continue;
}
deltaEnd = deltaStart;
SDL_SetRenderDrawColor(Renderer, BorderColor.R, BorderColor.G, BorderColor.B, 255);
SDL_RenderClear(Renderer);
SDL_SetRenderDrawColor(Renderer, 255, 255, 255, 255);
var texture = SDL_CreateTextureFromSurface(Renderer, VideoSurface);
SDL_SetRenderDrawBlendMode(Renderer, SDL_BlendMode.SDL_BLENDMODE_BLEND);
SDL_RenderCopy(Renderer, texture, 0, ref _outputRect);
SDL_DestroyTexture(texture);
SDL_RenderPresent(Renderer);
}
}
protected override void Draw(GameTime gameTime)
private void ProcessEvent(SDL_Event ev)
{
SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
GraphicsDevice.Clear(BorderColor);
SpriteBatch.DrawRectangle(renderTarget.Bounds.Location.ToVector2() + new Vector2(Borders.Left, Borders.Top),
new Size2(renderTarget.Bounds.Width * Scale, renderTarget.Bounds.Height * Scale), Color.Black,
Math.Min(renderTarget.Bounds.Width, renderTarget.Bounds.Height), 0);
SpriteBatch.Draw(renderTarget, new(Borders.Left, Borders.Top), null, Color.White, 0f, Vector2.Zero, Scale,
SpriteEffects.None, 0);
EventEmitter.RaiseOverlay(new()
switch (ev.type)
{
GameTime = gameTime,
TotalTicks = _totalTicks,
});
case SDL_EventType.SDL_QUIT:
_running = false;
break;
SpriteBatch.End();
case SDL_EventType.SDL_KEYDOWN:
break;
base.Draw(gameTime);
case SDL_EventType.SDL_MOUSEBUTTONUP:
case SDL_EventType.SDL_MOUSEBUTTONDOWN:
unsafe
{
var pitch = ((SDL_Surface*)VideoSurface)->pitch;
((uint*)((SDL_Surface*)VideoSurface)->pixels)[x + y * pitch / 4] = 0xFFFF00FF;
}
break;
}
}
public void Dispose()
{
SDL_DestroyRenderer(SurfaceRenderer);
SDL_FreeSurface(VideoSurface);
SDL_DestroyRenderer(Renderer);
SDL_DestroyWindow(Window);
SDL_Quit();
}
}

View file

@ -53,7 +53,7 @@ public class Audio : IDisposable
private static void EnqueueAudioNeedEvent(int i, int pending)
{
Capy64.Instance.LuaRuntime.QueueEvent("audio_need", LK =>
LegacyEntry.Instance.LuaRuntime.QueueEvent("audio_need", LK =>
{
LK.PushInteger(i);
LK.PushInteger(pending);

View file

@ -54,7 +54,7 @@ public class Drawing : IDisposable
public Drawing()
{
_fontSystem = new FontSystem();
_fontSystem.AddFont(File.ReadAllBytes(Path.Combine(Capy64.AssetsPath, "font.ttf")));
_fontSystem.AddFont(File.ReadAllBytes(Path.Combine(LegacyEntry.AssetsPath, "font.ttf")));
}
public void Begin()
@ -184,7 +184,7 @@ public class Drawing : IDisposable
public void Clear(Color? color = default)
{
Color finalColor = color ?? Color.Black;
Capy64.Instance.BorderColor = finalColor;
LegacyEntry.Instance.BorderColor = finalColor;
_graphicsDevice.Clear(finalColor);
}

View file

@ -19,6 +19,7 @@ using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using SDL2;
namespace Capy64.Eventing;
@ -74,7 +75,7 @@ public class InputManager
};
public Texture2D Texture { get; set; }
public static float WindowScale => Capy64.Instance.Scale;
public static float WindowScale => LegacyEntry.Instance.Scale;
public const int MouseScrollDelta = 120;
private Point mousePosition;
@ -84,17 +85,17 @@ public class InputManager
private Modifiers keyboardMods = 0;
private readonly HashSet<Keys> pressedKeys = new();
private readonly Game _game;
private readonly Capy64 _game;
private readonly EventEmitter _eventEmitter;
public InputManager(Game game, EventEmitter eventManager)
public InputManager(Capy64 game, EventEmitter eventManager)
{
_game = game;
_eventEmitter = eventManager;
_game.Window.KeyDown += OnKeyDown;
/*_game.Window.KeyDown += OnKeyDown;
_game.Window.KeyUp += OnKeyUp;
_game.Window.TextInput += OnTextInput;
_game.Window.TextInput += OnTextInput;*/
var mouseState = Mouse.GetState();
vMouseScroll = mouseState.ScrollWheelValue;
@ -108,12 +109,36 @@ public class InputManager
UpdateGamePad(GamePad.GetState(PlayerIndex.One), IsActive);
}
public void UpdateMouse(MouseState state, bool isActive)
public void UpdateMouseSDL(SDL.SDL_Event ev)
{
var position = new Point(ev.button.x, ev.button.y);
var rawPosition = position - new Point(LegacyEntry.Instance.Borders.Left, LegacyEntry.Instance.Borders.Top);
var pos = new Point((int)(rawPosition.X / WindowScale), (int)(rawPosition.Y / WindowScale)) + new Point(1, 1);
if (pos.X < 1 || pos.Y < 1 || pos.X > (_game.Width) || pos.Y > _game.Height)
return;
if (pos != mousePosition)
{
mousePosition = pos;
_eventEmitter.RaiseMouseMove(new()
{
Position = mousePosition,
PressedButtons = mouseButtonStates
.Where(q => q.Value == ButtonState.Pressed)
.Select(q => (int)q.Key)
.ToArray()
});
}
}
private void UpdateMouse(MouseState state, bool isActive)
{
if (!isActive)
return;
var rawPosition = state.Position - new Point(Capy64.Instance.Borders.Left, Capy64.Instance.Borders.Top);
var rawPosition = state.Position - new Point(LegacyEntry.Instance.Borders.Left, LegacyEntry.Instance.Borders.Top);
var pos = new Point((int)(rawPosition.X / WindowScale), (int)(rawPosition.Y / WindowScale)) + new Point(1, 1);
if (pos.X < 1 || pos.Y < 1 || pos.X > Texture.Width || pos.Y > Texture.Height)

View file

@ -28,7 +28,7 @@ public class DiscordIntegration : IComponent
public readonly bool Enabled;
private readonly IConfiguration _configuration;
public DiscordIntegration(Capy64 game)
public DiscordIntegration(LegacyEntry game)
{
_configuration = game.Configuration;
@ -42,7 +42,7 @@ public class DiscordIntegration : IComponent
Client.OnReady += OnReady;
Capy64.Instance.Discord = this;
LegacyEntry.Instance.Discord = this;
if (Enabled)
Client.Initialize();
@ -62,7 +62,7 @@ public class DiscordIntegration : IComponent
Assets = new Assets()
{
LargeImageKey = "image_large",
LargeImageText = "Capy64 " + Capy64.Version,
LargeImageText = "Capy64 " + LegacyEntry.Version,
SmallImageKey = "image_small"
}
});

338
Capy64/LegacyEntry.cs Normal file
View file

@ -0,0 +1,338 @@
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
// Copyright 2023 Alessandro "AlexDevs" Proto
//
// Licensed under the Apache License, Version 2.0 (the "License").
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using Capy64.API;
using Capy64.Core;
using Capy64.Eventing;
using Capy64.Extensions;
using Capy64.Integrations;
using Capy64.PluginManager;
using Capy64.Runtime;
using Microsoft.Extensions.Configuration;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using MonoGame.Extended;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using static Capy64.Utils;
namespace Capy64;
public enum EngineMode
{
Classic,
Free
}
public class LegacyEntry : Game
{
public const string Version = "1.1.0-beta";
public static class DefaultParameters
{
public const int Width = 318;
public const int Height = 240;
public const float Scale = 2f;
public const float BorderMultiplier = 1.5f;
public static readonly EngineMode EngineMode = EngineMode.Classic;
public const int ClassicTickrate = 30;
public const int FreeTickrate = 60;
}
public static readonly string AssemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
public static readonly string AssetsPath = Path.Combine(AssemblyPath, "Assets");
public static string AppDataPath
{
get
{
string baseDir =
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData,
Environment.SpecialFolderOption.Create) :
RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ?
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData,
Environment.SpecialFolderOption.Create) :
RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ?
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData,
Environment.SpecialFolderOption.Create) :
"./";
return Path.Combine(baseDir, "Capy64");
}
}
public static LegacyEntry Instance { get; private set; }
public LegacyEntry Game => this;
public EngineMode EngineMode { get; private set; } = EngineMode.Classic;
public IList<IComponent> NativePlugins { get; private set; }
public IList<IComponent> Plugins { get; private set; }
public int Width { get; set; } = DefaultParameters.Width;
public int Height { get; set; } = DefaultParameters.Height;
public float Scale { get; set; } = DefaultParameters.Scale;
public Drawing Drawing { get; private set; }
public Audio Audio { get; private set; }
public LuaState LuaRuntime { get; set; }
public Eventing.EventEmitter EventEmitter { get; private set; }
public DiscordIntegration Discord { get; set; }
public int TickRate => tickrate;
public IConfiguration Configuration { get; private set; }
public Color BorderColor { get; set; } = Color.Black;
public Borders Borders = new()
{
Top = 0,
Bottom = 0,
Left = 0,
Right = 0,
};
public SpriteBatch SpriteBatch;
private readonly InputManager _inputManager;
private RenderTarget2D renderTarget;
private readonly GraphicsDeviceManager _graphics;
private ulong _totalTicks = 0;
private int tickrate = 0;
private int everyTick => 60 / tickrate;
public LegacyEntry()
{
Instance = this;
_graphics = new GraphicsDeviceManager(this);
//Content.RootDirectory = "Content";
IsMouseVisible = true;
EventEmitter = new();
_inputManager = new(null, EventEmitter);
Drawing = new();
}
public void SetEngineMode(EngineMode mode)
{
switch (mode)
{
case EngineMode.Classic:
tickrate = DefaultParameters.ClassicTickrate;
Width = DefaultParameters.Width;
Height = DefaultParameters.Height;
Window.AllowUserResizing = false;
ResetBorder();
break;
case EngineMode.Free:
tickrate = DefaultParameters.FreeTickrate;
Window.AllowUserResizing = true;
break;
}
EngineMode = mode;
UpdateSize(true);
}
public void UpdateSize(bool resize = true)
{
if (resize)
{
_graphics.PreferredBackBufferWidth = (int)(Width * Scale) + Borders.Left + Borders.Right;
_graphics.PreferredBackBufferHeight = (int)(Height * Scale) + Borders.Top + Borders.Bottom;
_graphics.ApplyChanges();
}
renderTarget = new RenderTarget2D(
GraphicsDevice,
Width,
Height,
false,
GraphicsDevice.PresentationParameters.BackBufferFormat,
DepthFormat.Depth24, 0, RenderTargetUsage.PreserveContents);
Drawing.Canvas = renderTarget;
_inputManager.Texture = renderTarget;
EventEmitter.RaiseScreenSizeChange();
}
private void OnWindowSizeChange(object sender, EventArgs e)
{
if (EngineMode == EngineMode.Classic)
{
UpdateSize(true);
return;
}
var bounds = Window.ClientBounds;
Width = (int)(bounds.Width / Scale);
Height = (int)(bounds.Height / Scale);
if (Window.IsMaximized())
{
var vertical = bounds.Height - (Height * Scale);
var horizontal = bounds.Width - (Width * Scale);
Borders.Top = (int)Math.Floor(vertical / 2d);
Borders.Bottom = (int)Math.Ceiling(vertical / 2d);
Borders.Left = (int)Math.Floor(horizontal / 2d);
Borders.Right = (int)Math.Ceiling(horizontal / 2d);
UpdateSize(false);
}
else
{
ResetBorder();
UpdateSize();
}
}
private void ResetBorder()
{
var size = (int)(Scale * DefaultParameters.BorderMultiplier);
Borders = new Borders
{
Top = size,
Bottom = size,
Left = size,
Right = size
};
}
protected override void Initialize()
{
var configBuilder = new ConfigurationBuilder();
var settingsPath = Path.Combine(AppDataPath, "settings.json");
if (!Directory.Exists(AppDataPath))
{
Directory.CreateDirectory(AppDataPath);
}
if (!File.Exists(settingsPath))
{
File.Copy(Path.Combine(AssetsPath, "default.json"), settingsPath);
}
configBuilder.AddJsonFile(Path.Combine(AssetsPath, "default.json"), false);
configBuilder.AddJsonFile(settingsPath, false);
Configuration = configBuilder.Build();
Window.Title = "Capy64 " + Version;
Scale = Configuration.GetValue("Window:Scale", DefaultParameters.Scale);
ResetBorder();
UpdateSize();
Window.AllowUserResizing = true;
Window.ClientSizeChanged += OnWindowSizeChange;
InactiveSleepTime = new TimeSpan(0);
SetEngineMode(Configuration.GetValue<EngineMode>("EngineMode", DefaultParameters.EngineMode));
Audio = new Audio();
NativePlugins = GetNativePlugins();
var safeMode = Configuration.GetValue("SafeMode", false);
if (!safeMode)
Plugins = PluginLoader.LoadAllPlugins(Path.Combine(AppDataPath, "plugins"));
EventEmitter.RaiseInit();
base.Initialize();
}
private List<IComponent> GetNativePlugins()
{
var iType = typeof(IComponent);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => iType.IsAssignableFrom(p) && !p.IsInterface);
var plugins = new List<IComponent>();
foreach (var type in types)
{
var instance = (IComponent)Activator.CreateInstance(type, this);
plugins.Add(instance);
}
return plugins;
}
protected override void LoadContent()
{
SpriteBatch = new SpriteBatch(GraphicsDevice);
}
protected override void Update(GameTime gameTime)
{
Drawing.Begin();
// Register user input
_inputManager.Update(IsActive);
EventEmitter.RaiseTick(new()
{
GameTime = gameTime,
TotalTicks = _totalTicks,
IsActiveTick = (int)_totalTicks % everyTick == 0,
});
Drawing.End();
_totalTicks++;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
GraphicsDevice.Clear(BorderColor);
SpriteBatch.DrawRectangle(renderTarget.Bounds.Location.ToVector2() + new Vector2(Borders.Left, Borders.Top),
new Size2(renderTarget.Bounds.Width * Scale, renderTarget.Bounds.Height * Scale), Color.Black,
Math.Min(renderTarget.Bounds.Width, renderTarget.Bounds.Height), 0);
SpriteBatch.Draw(renderTarget, new(Borders.Left, Borders.Top), null, Color.White, 0f, Vector2.Zero, Scale,
SpriteEffects.None, 0);
EventEmitter.RaiseOverlay(new()
{
GameTime = gameTime,
TotalTicks = _totalTicks,
});
SpriteBatch.End();
base.Draw(gameTime);
}
}

View file

@ -46,7 +46,7 @@ internal class PluginLoader
{
if (typeof(IComponent).IsAssignableFrom(type))
{
IComponent result = Activator.CreateInstance(type, Capy64.Instance) as IComponent;
IComponent result = Activator.CreateInstance(type, LegacyEntry.Instance) as IComponent;
plugins.Add(result);
}
}

View file

@ -17,13 +17,13 @@ using Capy64;
if (args.Length > 0 && args[0] == "sdl")
{
using var game = new SDLEntry();
using var game = new Capy64.Capy64();
game.Run();
}
else
{
using var game = new Capy64.Capy64();
using var game = new Capy64.LegacyEntry();
game.Run();
}

View file

@ -25,8 +25,8 @@ public class AudioLib : IComponent
{
private const int queueLimit = 8;
private static Capy64 _game;
public AudioLib(Capy64 game)
private static LegacyEntry _game;
public AudioLib(LegacyEntry game)
{
_game = game;
_game.EventEmitter.OnClose += OnClose;

View file

@ -29,8 +29,8 @@ public class EventLib : IComponent
private static bool FrozenTaskAwaiter = false;
private static Capy64 _game;
public EventLib(Capy64 game)
private static LegacyEntry _game;
public EventLib(LegacyEntry game)
{
_game = game;
}

View file

@ -26,7 +26,7 @@ namespace Capy64.Runtime.Libraries;
public class FileSystemLib : IComponent
{
public static string DataPath = Path.Combine(Capy64.AppDataPath, "data");
public static string DataPath = Path.Combine(LegacyEntry.AppDataPath, "data");
public FileSystemLib()
{
@ -111,7 +111,7 @@ public class FileSystemLib : IComponent
new(), // NULL
};
public FileSystemLib(Capy64 _) { }
public FileSystemLib(LegacyEntry _) { }
public void LuaInit(Lua state)
{

View file

@ -28,8 +28,8 @@ namespace Capy64.Runtime.Libraries;
public class GPULib : IComponent
{
private static Capy64 _game;
public GPULib(Capy64 game)
private static LegacyEntry _game;
public GPULib(LegacyEntry game)
{
_game = game;
}
@ -657,7 +657,7 @@ public class GPULib : IComponent
Texture2D texture;
try
{
texture = Texture2D.FromFile(Capy64.Instance.Drawing.Canvas.GraphicsDevice, path);
texture = Texture2D.FromFile(LegacyEntry.Instance.Drawing.Canvas.GraphicsDevice, path);
}
catch (Exception e)
{

View file

@ -30,12 +30,12 @@ namespace Capy64.Runtime.Libraries;
#nullable enable
public class HTTPLib : IComponent
{
private static Capy64 _game = null!;
private static LegacyEntry _game = null!;
private static HttpClient _httpClient = null!;
private static long _requestId;
public static readonly HashSet<WebSocketClient.Client> WebSocketConnections = new();
public static readonly string UserAgent = $"Capy64/{Capy64.Version}";
public static readonly string UserAgent = $"Capy64/{LegacyEntry.Version}";
private static IConfiguration _configuration = null!;
private readonly LuaRegister[] Library = new LuaRegister[]
@ -57,7 +57,7 @@ public class HTTPLib : IComponent
},
new(),
};
public HTTPLib(Capy64 game)
public HTTPLib(LegacyEntry game)
{
_game = game;
_requestId = 0;

View file

@ -24,8 +24,8 @@ namespace Capy64.Runtime.Libraries;
public class MachineLib : IComponent
{
private static Capy64 _game;
public MachineLib(Capy64 game)
private static LegacyEntry _game;
public MachineLib(LegacyEntry game)
{
_game = game;
}
@ -109,7 +109,7 @@ public class MachineLib : IComponent
{
var L = Lua.FromIntPtr(state);
var currentTitle = Capy64.Instance.Window.Title;
var currentTitle = LegacyEntry.Instance.Window.Title;
if (!L.IsNoneOrNil(1))
{
@ -117,12 +117,12 @@ public class MachineLib : IComponent
if (string.IsNullOrEmpty(newTitle))
{
newTitle = "Capy64 " + Capy64.Version;
newTitle = "Capy64 " + LegacyEntry.Version;
}
newTitle = newTitle[..Math.Min(newTitle.Length, 256)];
Capy64.Instance.Window.Title = newTitle;
LegacyEntry.Instance.Window.Title = newTitle;
}
L.PushString(currentTitle);
@ -157,7 +157,7 @@ public class MachineLib : IComponent
{
var L = Lua.FromIntPtr(state);
L.PushString("Capy64 " + Capy64.Version);
L.PushString("Capy64 " + LegacyEntry.Version);
return 1;
}
@ -171,7 +171,7 @@ public class MachineLib : IComponent
try
{
Capy64.Instance.Discord.SetPresence(details, dstate);
LegacyEntry.Instance.Discord.SetPresence(details, dstate);
L.PushBoolean(true);
}
catch (Exception e)

View file

@ -49,11 +49,11 @@ internal class TermLib : IComponent
public static Color BackgroundColor { get; set; }
private static Char?[] CharGrid;
private static Capy64 _game;
private static LegacyEntry _game;
private static bool cursorState = false;
private static bool enableCursor = true;
private static Texture2D cursorTexture;
public TermLib(Capy64 game)
public TermLib(LegacyEntry game)
{
_game = game;
@ -317,7 +317,7 @@ internal class TermLib : IComponent
{
var realpos = ToRealPos(CursorPosition - Vector2.One);
var charpos = (realpos * _game.Scale) + ((CharOffset + new Vector2(0, 2)) * _game.Scale);
charpos += new Vector2(Capy64.Instance.Borders.Left, Capy64.Instance.Borders.Top);
charpos += new Vector2(LegacyEntry.Instance.Borders.Left, LegacyEntry.Instance.Borders.Top);
_game.Game.SpriteBatch.Draw(cursorTexture, charpos, null, ForegroundColor, 0f, Vector2.Zero, _game.Scale, SpriteEffects.None, 0);
}
}

View file

@ -50,11 +50,11 @@ class TimerLib : IComponent
new(),
};
private static Capy64 _game;
private static LegacyEntry _game;
private static uint _timerId = 0;
private static readonly ConcurrentDictionary<uint, Timer> timers = new();
public TimerLib(Capy64 game)
public TimerLib(LegacyEntry game)
{
_game = game;
@ -122,7 +122,7 @@ class TimerLib : IComponent
timers[timerId] = new Timer
{
RemainingTicks = (int)(delay * Capy64.Instance.TickRate)
RemainingTicks = (int)(delay * LegacyEntry.Instance.TickRate)
};
L.PushInteger(timerId);
@ -141,7 +141,7 @@ class TimerLib : IComponent
timers[timerId] = new Timer
{
RemainingTicks = (int)(delay * Capy64.Instance.TickRate),
RemainingTicks = (int)(delay * LegacyEntry.Instance.TickRate),
Task = task,
};

View file

@ -72,8 +72,8 @@ public class LuaState : IDisposable
private void InitPlugins()
{
var allPlugins = new List<IComponent>(Capy64.Instance.NativePlugins);
allPlugins.AddRange(Capy64.Instance.Plugins);
var allPlugins = new List<IComponent>(LegacyEntry.Instance.NativePlugins);
allPlugins.AddRange(LegacyEntry.Instance.Plugins);
foreach (var plugin in allPlugins)
{
plugin.LuaInit(Thread);

View file

@ -25,8 +25,8 @@ public class ObjectManager : IComponent
{
private static readonly ConcurrentDictionary<nint, object> _objects = new();
private static Capy64 _game;
public ObjectManager(Capy64 game)
private static LegacyEntry _game;
public ObjectManager(LegacyEntry game)
{
_game = game;
_game.EventEmitter.OnClose += OnClose;

View file

@ -88,7 +88,7 @@ public class FileHandle : IComponent
new(),
};
public FileHandle(Capy64 _) { }
public FileHandle(LegacyEntry _) { }
public void LuaInit(Lua L)
{

View file

@ -66,8 +66,8 @@ public class GPUBufferMeta : IComponent
new(),
};
private static Capy64 _game;
public GPUBufferMeta(Capy64 game)
private static LegacyEntry _game;
public GPUBufferMeta(LegacyEntry game)
{
_game = game;
}

View file

@ -29,8 +29,8 @@ public class Socket : IDisposable
public class SocketLib : IComponent
{
private static Capy64 _game = null!;
public SocketLib(Capy64 game)
private static LegacyEntry _game = null!;
public SocketLib(LegacyEntry game)
{
_game = game;
}

View file

@ -21,8 +21,8 @@ namespace Capy64.Runtime.Objects;
public class TaskMeta : IComponent
{
private static Capy64 _game;
public TaskMeta(Capy64 game)
private static LegacyEntry _game;
public TaskMeta(LegacyEntry game)
{
_game = game;
}

View file

@ -73,7 +73,7 @@ public class WebSocketClient : IComponent
new(),
};
public WebSocketClient(Capy64 _) { }
public WebSocketClient(LegacyEntry _) { }
public void LuaInit(Lua L)
{
@ -146,7 +146,7 @@ public class WebSocketClient : IComponent
.ContinueWith(async task =>
{
await task;
Capy64.Instance.LuaRuntime.QueueEvent("websocket_close", LK =>
LegacyEntry.Instance.LuaRuntime.QueueEvent("websocket_close", LK =>
{
LK.PushInteger(client.RequestId);

View file

@ -32,8 +32,8 @@ internal class RuntimeManager : IComponent
private static bool close = false;
private static bool inPanic = false;
private static Capy64 _game;
public RuntimeManager(Capy64 game)
private static LegacyEntry _game;
public RuntimeManager(LegacyEntry game)
{
_game = game;
@ -124,7 +124,7 @@ internal class RuntimeManager : IComponent
luaState.Thread.PushCFunction(L_Exit);
luaState.Thread.SetGlobal("exit");
var status = luaState.Thread.LoadFile(Path.Combine(Capy64.AssetsPath, "Lua/bios.lua"));
var status = luaState.Thread.LoadFile(Path.Combine(LegacyEntry.AssetsPath, "Lua/bios.lua"));
if (status != LuaStatus.OK)
{
throw new LuaException(luaState.Thread.ToString(-1));
@ -185,7 +185,7 @@ internal class RuntimeManager : IComponent
private void LoadFirmware()
{
var firmwareContent = File.ReadAllText(Path.Combine(Capy64.AssetsPath, "Lua/firmware.lua"));
var firmwareContent = File.ReadAllText(Path.Combine(LegacyEntry.AssetsPath, "Lua/firmware.lua"));
var errored = luaState.Thread.DoString(firmwareContent);
if (errored)
{
@ -212,10 +212,10 @@ internal class RuntimeManager : IComponent
public static void InstallOS(bool force = false)
{
var installedFilePath = Path.Combine(Capy64.AppDataPath, ".installed");
var installedFilePath = Path.Combine(LegacyEntry.AppDataPath, ".installed");
if (!File.Exists(installedFilePath) || force)
{
FileSystemLib.CopyDirectory(Path.Combine(Capy64.AssetsPath, "Lua/CapyOS"), FileSystemLib.DataPath, true, true);
FileSystemLib.CopyDirectory(Path.Combine(LegacyEntry.AssetsPath, "Lua/CapyOS"), FileSystemLib.DataPath, true, true);
File.Create(installedFilePath).Dispose();
}
}

View file

@ -1,171 +0,0 @@
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
// Copyright 2023 Alessandro "AlexDevs" Proto
//
// Licensed under the Apache License, Version 2.0 (the "License").
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
namespace Capy64;
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using static global::Capy64.Utils;
using static SDL2.SDL;
public class SDLEntry : IDisposable
{
public const string Version = "1.1.0-beta";
public nint Window { get; private set; } = 0;
public nint Renderer { get; private set; } = 0;
public nint VideoSurface { get; private set; } = 0;
public int WindowWidth { get; private set; }
public int WindowHeight { get; private set; }
public int Width { get; set; } = DefaultParameters.Width;
public int Height { get; set; } = DefaultParameters.Height;
public float Scale { get; set; } = DefaultParameters.Scale;
public uint BorderColor { get; set; } = 0x0;
public Borders Borders
{
get => _borders;
set
{
_borders = value;
}
}
private SDL_Rect _bordersRect;
private Borders _borders;
public static readonly string AssemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
public static readonly string AssetsPath = Path.Combine(AssemblyPath, "Assets");
public static class DefaultParameters
{
public const int Width = 318;
public const int Height = 240;
public const float Scale = 2f;
public const float BorderMultiplier = 1.5f;
public static readonly EngineMode EngineMode = EngineMode.Classic;
public const int ClassicTickrate = 30;
public const int FreeTickrate = 60;
}
public static string AppDataPath
{
get
{
string baseDir =
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData,
Environment.SpecialFolderOption.Create) :
RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ?
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData,
Environment.SpecialFolderOption.Create) :
RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ?
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData,
Environment.SpecialFolderOption.Create) :
"./";
return Path.Combine(baseDir, "Capy64");
}
}
public SDLEntry()
{
Borders = new()
{
Top = 0,
Bottom = 0,
Left = 0,
Right = 0,
};
WindowWidth = (int)(Width * Scale) + Borders.Left + Borders.Right;
WindowHeight = (int)(Height * Scale) + Borders.Top + Borders.Bottom;
}
public void Run()
{
#if DEBUG
SDL_SetHint(SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING, "1");
#endif
if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
{
Console.WriteLine(SDL_GetError());
return;
}
Window = SDL_CreateWindow("Capy64 " + Version,
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
WindowWidth, WindowHeight,
SDL_WindowFlags.SDL_WINDOW_OPENGL);
if (Window == nint.Zero)
{
Console.WriteLine(SDL_GetError());
return;
}
Renderer = SDL_CreateRenderer(Window,
0,
SDL_RendererFlags.SDL_RENDERER_ACCELERATED | SDL_RendererFlags.SDL_RENDERER_PRESENTVSYNC);
if (Renderer == nint.Zero)
{
Console.WriteLine(SDL_GetError());
return;
}
VideoSurface = SDL_CreateRGBSurfaceWithFormat(0, Width, Height, 32, SDL_PIXELFORMAT_ARGB8888);
var running = true;
while (running)
{
while (SDL_PollEvent(out var ev) != 0)
{
switch (ev.type)
{
case SDL_EventType.SDL_QUIT:
running = false;
break;
case SDL_EventType.SDL_KEYDOWN:
if (ev.key.keysym.scancode == SDL_Scancode.SDL_SCANCODE_ESCAPE)
running = false;
unsafe
{
var pitch = ((SDL_Surface*)VideoSurface)->pitch;
((uint*)((SDL_Surface*)VideoSurface)->pixels)[10 + 10 * pitch / 4] = 0xFFFF00FF;
}
break;
}
}
SDL_RenderClear(Renderer);
var texture = SDL_CreateTextureFromSurface(Renderer, VideoSurface);
SDL_RenderCopy(Renderer, texture, 0, 0);
SDL_DestroyTexture(texture);
SDL_RenderPresent(Renderer);
}
}
public void Dispose()
{
SDL_DestroyRenderer(Renderer);
SDL_DestroyWindow(Window);
SDL_Quit();
}
}

View file

@ -5,8 +5,8 @@ namespace ExamplePlugin;
public class MyPlugin : IComponent
{
private static Capy64.Capy64 _game;
public MyPlugin(Capy64.Capy64 game)
private static Capy64.LegacyEntry _game;
public MyPlugin(Capy64.LegacyEntry game)
{
_game = game;
}