initial SDL platform implementation support

yay for code-dumps!
This commit is contained in:
Gered 2013-08-17 15:15:24 -04:00
parent 1b4abf1bfe
commit 6e321c77f3
17 changed files with 986 additions and 40 deletions

View file

@ -49,6 +49,10 @@
<Compile Include="IO\SDLFileSystem.cs" />
<Compile Include="SDLLogger.cs" />
<Compile Include="SDLLooper.cs" />
<Compile Include="SDLWindow.cs" />
<Compile Include="SDLConfiguration.cs" />
<Compile Include="SDLPlatformServices.cs" />
<Compile Include="CurrentOS.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>

View file

@ -0,0 +1,152 @@
//
// CurrentOS Class by blez
// http://blez.wordpress.com/2012/09/17/determine-os-with-netmono/
// Detects the current OS (Windows, Linux, MacOS)
//
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;
namespace Blarg.GameFramework
{
public static class CurrentOS
{
public static bool IsWindows { get; private set; }
public static bool IsUnix { get; private set; }
public static bool IsMac { get; private set; }
public static bool IsLinux { get; private set; }
public static bool IsUnknown { get; private set; }
public static bool Is32bit { get; private set; }
public static bool Is64bit { get; private set; }
public static bool Is64BitProcess { get { return (IntPtr.Size == 8); } }
public static bool Is32BitProcess { get { return (IntPtr.Size == 4); } }
public static string Name { get; private set; }
[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWow64Process([In] IntPtr hProcess, [Out] out bool wow64Process);
private static bool Is64bitWindows
{
get
{
if ((Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor >= 1) || Environment.OSVersion.Version.Major >= 6)
{
using (Process p = Process.GetCurrentProcess())
{
bool retVal;
if (!IsWow64Process(p.Handle, out retVal))
return false;
return retVal;
}
}
else
return false;
}
}
static CurrentOS()
{
IsWindows = Path.DirectorySeparatorChar == '\\';
if (IsWindows)
{
Name = Environment.OSVersion.VersionString;
Name = Name.Replace("Microsoft ", "");
Name = Name.Replace(" ", " ");
Name = Name.Replace(" )", ")");
Name = Name.Trim();
Name = Name.Replace("NT 6.2", "8 %bit 6.2");
Name = Name.Replace("NT 6.1", "7 %bit 6.1");
Name = Name.Replace("NT 6.0", "Vista %bit 6.0");
Name = Name.Replace("NT 5.", "XP %bit 5.");
Name = Name.Replace("%bit", (Is64bitWindows ? "64bit" : "32bit"));
if (Is64bitWindows)
Is64bit = true;
else
Is32bit = true;
}
else
{
string UnixName = ReadProcessOutput("uname");
if (UnixName.Contains("Darwin"))
{
IsUnix = true;
IsMac = true;
Name = "MacOS X " + ReadProcessOutput("sw_vers", "-productVersion");
Name = Name.Trim();
string machine = ReadProcessOutput("uname", "-m");
if (machine.Contains("x86_64"))
Is64bit = true;
else
Is32bit = true;
Name += " " + (Is32bit ? "32bit" : "64bit");
}
else if (UnixName.Contains("Linux"))
{
IsUnix = true;
IsLinux = true;
Name = ReadProcessOutput("lsb_release", "-d");
Name = Name.Substring(Name.IndexOf(":") + 1);
Name = Name.Trim();
string machine = ReadProcessOutput("uname", "-m");
if (machine.Contains("x86_64"))
Is64bit = true;
else
Is32bit = true;
Name += " " + (Is32bit ? "32bit" : "64bit");
}
else if (UnixName != "")
IsUnix = true;
else
IsUnknown = true;
}
}
private static string ReadProcessOutput(string name)
{
return ReadProcessOutput(name, null);
}
private static string ReadProcessOutput(string name, string args)
{
try
{
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
if (args != null && args != "")
p.StartInfo.Arguments = " " + args;
p.StartInfo.FileName = name;
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
// p.WaitForExit();
// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
if (output == null)
output = "";
output = output.Trim();
return output;
}
catch
{
return "";
}
}
}
}

View file

@ -0,0 +1,36 @@
using System;
namespace Blarg.GameFramework
{
public class SDLConfiguration : IPlatformConfiguration
{
public string Title;
public int Width;
public int Height;
public bool Fullscreen;
public bool Resizeable;
public bool glDoubleBuffer;
public int glDepthBufferSize;
public int glRedSize;
public int glGreenSize;
public int glBlueSize;
public int glAlphaSize;
public SDLConfiguration()
{
Title = "SDL Application";
Width = 854;
Height = 480;
Fullscreen = false;
Resizeable = false;
glDoubleBuffer = true;
glDepthBufferSize = 24;
glRedSize = 8;
glGreenSize = 8;
glBlueSize = 8;
glAlphaSize = 8;
}
}
}

View file

@ -1,84 +1,578 @@
using System;
using System.Diagnostics;
using SDL2;
using Blarg.GameFramework.Graphics;
using Blarg.GameFramework.Input;
using Blarg.GameFramework.IO;
namespace Blarg.GameFramework
{
public class SDLLooper : ILooper
public class SDLLooper : BaseLooper
{
const string LOOPER_TAG = "SDLLOOPER";
bool _isSDLinited;
IntPtr _window;
IntPtr _glContext;
SDLKeyboard _keyboard;
SDLMouse _mouse;
SDLFileSystem _filesystem;
SDLWindow _windowInfo;
bool _isWindowActive;
bool _isPaused;
bool _isQuitting;
int _fps;
float _frameTime;
int _rendersPerSecond;
int _updatesPerSecond;
int _renderTime;
int _updateTime;
int _targetUpdatesPerSecond;
int _ticksPerUpdate;
float _fixedUpdateInterval;
float _fixedRenderInterval;
int _maxFrameSkip = 10;
public override int FPS
{
get { return _fps; }
}
public override float FrameTime
{
get { return _frameTime; }
}
public override int RendersPerSecond
{
get { return _rendersPerSecond; }
}
public override int UpdatesPerSecond
{
get { return _updatesPerSecond; }
}
public override int RenderTime
{
get { return _renderTime; }
}
public override int UpdateTime
{
get { return _updateTime; }
}
public SDLLooper()
{
Platform.Services = new SDLPlatformServices();
_windowInfo = new SDLWindow();
SetUpdateFrequency(60);
}
public void Run(IGameApp gameApp)
public override void Run(IGameApp gameApp, IPlatformConfiguration config)
{
if (gameApp == null)
throw new ArgumentNullException("gameApp");
GameApp = gameApp;
Platform.Services.Logger.Info("Looper", "Running...");
if (config == null)
throw new ArgumentNullException("config");
if (!(config is SDLConfiguration))
throw new ArgumentException("Must pass a SDLConfiguration object.", "config");
Platform.Services.Logger.Info(LOOPER_TAG, "Running...");
SDLConfiguration sdlConfig = (SDLConfiguration)config;
Platform.Services.Logger.Info(LOOPER_TAG, "Received SDL configuration:");
Platform.Services.Logger.Info(LOOPER_TAG, "\tTitle: {0}", sdlConfig.Title);
Platform.Services.Logger.Info(LOOPER_TAG, "\tWidth: {0}", sdlConfig.Width);
Platform.Services.Logger.Info(LOOPER_TAG, "\tHeight: {0}", sdlConfig.Height);
Platform.Services.Logger.Info(LOOPER_TAG, "\tFullscreen: {0}", sdlConfig.Fullscreen);
Platform.Services.Logger.Info(LOOPER_TAG, "\tResizeable: {0}", sdlConfig.Resizeable);
Platform.Services.Logger.Info(LOOPER_TAG, "GL Doublebuffer: {0}", sdlConfig.glDoubleBuffer);
Platform.Services.Logger.Info(LOOPER_TAG, "GL Depth Buffer Size: {0}", sdlConfig.glDepthBufferSize);
Platform.Services.Logger.Info(LOOPER_TAG, "GL Red Size: {0}", sdlConfig.glRedSize);
Platform.Services.Logger.Info(LOOPER_TAG, "GL Green Size: {0}", sdlConfig.glGreenSize);
Platform.Services.Logger.Info(LOOPER_TAG, "GL Blue Size: {0}", sdlConfig.glBlueSize);
Platform.Services.Logger.Info(LOOPER_TAG, "GL Alpha Size: {0}", sdlConfig.glAlphaSize);
if (!InitSDL())
{
Platform.Services.Logger.Error("Looper", "SDL initialization failed. Aborting.");
Platform.Services.Logger.Error(LOOPER_TAG, "SDL initialization failed. Aborting.");
return;
}
if (!InitSDLWindow(sdlConfig))
{
Platform.Services.Logger.Error(LOOPER_TAG, "SDL window creation failed. Aborting.");
return;
}
(Platform.Services as SDLPlatformServices).Keyboard = _keyboard;
(Platform.Services as SDLPlatformServices).Mouse = _mouse;
(Platform.Services as SDLPlatformServices).FileSystem = _filesystem;
(Platform.Services as SDLPlatformServices).GL = new PortableGL.SDL.SDLGL20();
OnNewContext();
OnResize(ScreenOrientation.Rotation0, _windowInfo.ClientRectangle);
OnLoad();
Platform.Services.Logger.Info(LOOPER_TAG, "Main loop starting.");
MainLoop();
Platform.Services.Logger.Info(LOOPER_TAG, "Main loop finished.");
OnUnload();
OnLostContext();
GameApp.Dispose();
GameApp = null;
ReleaseSDL();
}
private bool InitSDL()
{
Platform.Services.Logger.Info("Looper", "SDL initialization starting.");
Platform.Services.Logger.Info(LOOPER_TAG, "SDL initialization starting.");
SDL.SDL_version sdlVersion;
SDL.SDL_VERSION(out sdlVersion);
Platform.Services.Logger.Info("Looper", "SDL Runtime Version: {0}.{1}.{2}", sdlVersion.major, sdlVersion.minor, sdlVersion.patch);
Platform.Services.Logger.Info("Looper", "SDL Linked Version: {0}.{1}.{2}", SDL.SDL_MAJOR_VERSION, SDL.SDL_MINOR_VERSION, SDL.SDL_PATCHLEVEL);
Platform.Services.Logger.Info(LOOPER_TAG, "SDL Runtime Version: {0}.{1}.{2}", sdlVersion.major, sdlVersion.minor, sdlVersion.patch);
Platform.Services.Logger.Info(LOOPER_TAG, "SDL Linked Version: {0}.{1}.{2}", SDL.SDL_MAJOR_VERSION, SDL.SDL_MINOR_VERSION, SDL.SDL_PATCHLEVEL);
if (SDL.SDL_Init(SDL.SDL_INIT_VIDEO | SDL.SDL_INIT_AUDIO | SDL.SDL_INIT_GAMECONTROLLER | SDL.SDL_INIT_JOYSTICK | SDL.SDL_INIT_TIMER) == -1)
{
Platform.Services.Logger.Error("Looper", "SDL_Init() failed: {0}", SDL.SDL_GetError());
Platform.Services.Logger.Error(LOOPER_TAG, "SDL_Init() failed: {0}", SDL.SDL_GetError());
return false;
}
_isSDLinited = true;
_keyboard = new SDLKeyboard();
Platform.Services.Logger.Info("Looper", "Keyboard input device ready.");
Platform.Services.Logger.Info(LOOPER_TAG, "Keyboard input device ready.");
_mouse = new SDLMouse();
Platform.Services.Logger.Info("Looper", "Mouse input device ready.");
Platform.Services.Logger.Info(LOOPER_TAG, "Mouse input device ready.");
int numJoysticks = SDL.SDL_NumJoysticks();
Platform.Services.Logger.Info("Looper", "{0} joystick input devices found.", numJoysticks);
Platform.Services.Logger.Info(LOOPER_TAG, "{0} joystick input devices found.", numJoysticks);
for (int i = 0; i < numJoysticks; ++i)
{
Platform.Services.Logger.Info("Looper", "Joystick #{0}. {1}:", (i + 1), SDL.SDL_JoystickNameForIndex(i));
Platform.Services.Logger.Info(LOOPER_TAG, "Joystick #{0}. {1}:", (i + 1), SDL.SDL_JoystickNameForIndex(i));
IntPtr joystick = SDL.SDL_JoystickOpen(i);
if (joystick != IntPtr.Zero)
{
Platform.Services.Logger.Info("Looper", "\tAxes: {0}", SDL.SDL_JoystickNumAxes(joystick));
Platform.Services.Logger.Info("Looper", "\tBalls: {0}", SDL.SDL_JoystickNumBalls(joystick));
Platform.Services.Logger.Info("Looper", "\tHats: {0}", SDL.SDL_JoystickNumHats(joystick));
Platform.Services.Logger.Info("Looper", "\tButtons: {0}", SDL.SDL_JoystickNumButtons(joystick));
Platform.Services.Logger.Info(LOOPER_TAG, "\tAxes: {0}", SDL.SDL_JoystickNumAxes(joystick));
Platform.Services.Logger.Info(LOOPER_TAG, "\tBalls: {0}", SDL.SDL_JoystickNumBalls(joystick));
Platform.Services.Logger.Info(LOOPER_TAG, "\tHats: {0}", SDL.SDL_JoystickNumHats(joystick));
Platform.Services.Logger.Info(LOOPER_TAG, "\tButtons: {0}", SDL.SDL_JoystickNumButtons(joystick));
SDL.SDL_JoystickClose(joystick);
}
else
Platform.Services.Logger.Warn("Looper", "\tMore information could not be obtained.");
Platform.Services.Logger.Warn(LOOPER_TAG, "\tMore information could not be obtained.");
}
_filesystem = new SDLFileSystem();
Platform.Services.Logger.Info("Looper", "Filesystem access initialized.");
Platform.Services.Logger.Info(LOOPER_TAG, "Filesystem access initialized.");
Platform.Services.Logger.Info("Looper", "SDL initialization finished.");
int numVideoDrivers = SDL.SDL_GetNumVideoDrivers();
Platform.Services.Logger.Info(LOOPER_TAG, "Video drivers present: {0}.", numVideoDrivers);
for (int i = 0; i < numVideoDrivers; ++i)
Platform.Services.Logger.Info(LOOPER_TAG, "\t{0}: {1}", (i + 1), SDL.SDL_GetVideoDriver(i));
Platform.Services.Logger.Info(LOOPER_TAG, "Currently using video driver: {0}", SDL.SDL_GetCurrentVideoDriver());
int numAudioDrivers = SDL.SDL_GetNumAudioDrivers();
Platform.Services.Logger.Info(LOOPER_TAG, "Audio drivers present: {0}", numAudioDrivers);
for (int i = 0; i < numAudioDrivers; ++i)
Platform.Services.Logger.Info(LOOPER_TAG, "\t{0}: {1}", (i + 1), SDL.SDL_GetAudioDriver(i));
Platform.Services.Logger.Info(LOOPER_TAG, "Currently using audio driver: {0}", SDL.SDL_GetCurrentAudioDriver());
Platform.Services.Logger.Info(LOOPER_TAG, "SDL initialization finished.");
return true;
}
private void SetUpdateFrequency(int targetFrequency)
{
_targetUpdatesPerSecond = targetFrequency;
_ticksPerUpdate = 1000 / _targetUpdatesPerSecond;
_fixedUpdateInterval = _ticksPerUpdate / 1000.0f;
}
private void MainLoop()
{
_isWindowActive = true;
_isPaused = false;
_isQuitting = false;
int numUpdatesThisFrame = 0;
int numLoops = 0;
int timeElapsed = 0;
bool isDirty = false;
bool isRunningSlowly = false;
int updateTime = 0;
int renderTime = 0;
int numUpdates = 0;
int numRenders = 0;
int nextUpdateAt = Environment.TickCount;
int currentTime = Environment.TickCount;
while (!_isQuitting)
{
if (_isPaused)
{
// we always want to be processing events, even when paused
DoEvents();
}
else
{
int newTime = Environment.TickCount;
int frameTime = newTime - currentTime;
currentTime = newTime;
timeElapsed += frameTime;
// every second recalculate the FPS
if (timeElapsed >= 1000)
{
_fps = numLoops;
_frameTime = 1000.0f / _fps;
_rendersPerSecond = numRenders;
_updatesPerSecond = numUpdates;
_renderTime = renderTime;
_updateTime = updateTime;
numUpdates = 0;
numRenders = 0;
numLoops = 0;
timeElapsed = 0;
}
// we're "running slowly" if we're more then one update behind
if (currentTime > nextUpdateAt + _ticksPerUpdate)
isRunningSlowly = true;
else
isRunningSlowly = false;
numUpdatesThisFrame = 0;
while (Environment.TickCount >= nextUpdateAt && numUpdatesThisFrame < _maxFrameSkip)
{
if (numUpdatesThisFrame > 0)
isRunningSlowly = true;
int before = Environment.TickCount;
DoEvents();
OnUpdate(_fixedUpdateInterval);
updateTime += Environment.TickCount - before;
++numUpdatesThisFrame;
nextUpdateAt += _ticksPerUpdate;
++numUpdates;
// just updated, so we need to render the new game state
isDirty = true;
}
if (isDirty && _isWindowActive)
{
int before = Environment.TickCount;
OnRender(_fixedRenderInterval); // TODO
SDL.SDL_GL_SwapWindow(_window);
renderTime += Environment.TickCount - before;
++numRenders;
// don't render again until we hve something new to show
isDirty = false;
}
++numLoops;
}
}
}
#region Window Management
private bool InitSDLWindow(SDLConfiguration config)
{
Platform.Services.Logger.Info(LOOPER_TAG, "SDL Window initialization starting.");
int flags = (int)SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL | (int)SDL.SDL_WindowFlags.SDL_WINDOW_SHOWN;
if (config.Fullscreen)
flags |= (int)SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN;
if (config.Resizeable)
flags |= (int)SDL.SDL_WindowFlags.SDL_WINDOW_RESIZABLE;
if (!CreateWindow(config.Title, config.Width, config.Height, flags))
return false;
if (!CreateOpenGLContext(config))
return false;
Platform.Services.Logger.Info(LOOPER_TAG, "SDL Window initialization finished.");
return true;
}
private bool CreateWindow(string title, int width, int height, int flags)
{
Platform.Services.Logger.Info(LOOPER_TAG, "Attempting to set up new window with dimensions {0}x{1}.", width, height);
if (_window != IntPtr.Zero)
throw new InvalidOperationException("Cannot create new window before destorying existing one.");
_window = SDL.SDL_CreateWindow(title, SDL.SDL_WINDOWPOS_UNDEFINED, SDL.SDL_WINDOWPOS_UNDEFINED, width, height, (SDL.SDL_WindowFlags)flags);
if (_window == IntPtr.Zero)
{
Platform.Services.Logger.Error(LOOPER_TAG, "Window creation failed: {0}", SDL.SDL_GetError());
return false;
}
SetWindowInfo();
Platform.Services.Logger.Info(LOOPER_TAG, "Window creation succeeded.");
return true;
}
private bool DestroyWindow()
{
Platform.Services.Logger.Info(LOOPER_TAG, "Destroying window.");
if (_window == IntPtr.Zero)
{
Platform.Services.Logger.Warn(LOOPER_TAG, "No window currently exists, not doing anything.");
return true;
}
SDL.SDL_DestroyWindow(_window);
_window = IntPtr.Zero;
Platform.Services.Logger.Info(LOOPER_TAG, "Window destroyed.");
return true;
}
private void SetWindowInfo()
{
if (_window == IntPtr.Zero)
throw new InvalidOperationException("Cannot set window info for a non-existant window.");
int windowX;
int windowY;
SDL.SDL_GetWindowPosition(_window, out windowX, out windowY);
int clientWidth;
int clientHeight;
SDL.SDL_GetWindowSize(_window, out clientWidth, out clientHeight);
_windowInfo.ClientWidth = clientWidth;
_windowInfo.ClientHeight = clientHeight;
_windowInfo.ClientRectangle = new Rect(0, 0, clientWidth, clientHeight);
Platform.Services.Logger.Info(LOOPER_TAG, "Window content area set to {0}", _windowInfo.ClientRectangle);
}
#endregion
#region OpenGL Context Management
private bool CreateOpenGLContext(SDLConfiguration config)
{
Platform.Services.Logger.Info(LOOPER_TAG, "Attempting to create OpenGL context.");
if (_glContext != IntPtr.Zero)
throw new InvalidOperationException("Cannoy create new OpenGL context before destroying existing one.");
if (_window == IntPtr.Zero)
throw new InvalidOperationException("Cannot create an OpenGL context without an existing window.");
// minimum requirements
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_DOUBLEBUFFER, config.glDoubleBuffer ? 1 : 0);
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_DEPTH_SIZE, config.glDepthBufferSize);
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_RED_SIZE, config.glRedSize);
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_GREEN_SIZE, config.glGreenSize);
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_BLUE_SIZE, config.glBlueSize);
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_ALPHA_SIZE, config.glAlphaSize);
_glContext = SDL.SDL_GL_CreateContext(_window);
if (_glContext == IntPtr.Zero)
{
Platform.Services.Logger.Error(LOOPER_TAG, "OpenGL context creation failed: {0}", SDL.SDL_GetError());
return false;
}
Platform.Services.Logger.Info(LOOPER_TAG, "OpenGL context creation succeeded.");
Platform.Services.Logger.Info(LOOPER_TAG, "Setting OpenTK's OpenGL context and loading OpenGL extensions.");
OpenTK.Graphics.GraphicsContext.CurrentContext = _glContext;
OpenTK.Graphics.OpenGL.GL.LoadAll();
int redSize;
int greenSize;
int blueSize;
int alphaSize;
int bufferSize;
int doubleBuffer;
int depthSize;
int stencilSize;
int accumRedSize;
int accumGreenSize;
int accumBlueSize;
int accumAlphaSize;
int stereo;
int multisampleBuffers;
int multisampleSamples;
int acceleratedVisual;
int contextMajorVersion;
int contextMinorVersion;
SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_RED_SIZE, out redSize);
SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_GREEN_SIZE, out greenSize);
SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_BLUE_SIZE, out blueSize);
SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_ALPHA_SIZE, out alphaSize);
SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_BUFFER_SIZE, out bufferSize);
SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_DOUBLEBUFFER, out doubleBuffer);
SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_DEPTH_SIZE, out depthSize);
SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_STENCIL_SIZE, out stencilSize);
SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_ACCUM_RED_SIZE, out accumRedSize);
SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_ACCUM_GREEN_SIZE, out accumGreenSize);
SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_ACCUM_BLUE_SIZE, out accumBlueSize);
SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_ACCUM_ALPHA_SIZE, out accumAlphaSize);
SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_STEREO, out stereo);
SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_MULTISAMPLEBUFFERS, out multisampleBuffers);
SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_MULTISAMPLESAMPLES, out multisampleSamples);
SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_ACCELERATED_VISUAL, out acceleratedVisual);
SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, out contextMajorVersion);
SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, out contextMinorVersion);
Platform.Services.Logger.Info(LOOPER_TAG, "OpenGL context attributes:");
Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_RED_SIZE: {0}", redSize);
Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_GREEN_SIZE: {0}", greenSize);
Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_BLUE_SIZE: {0}", blueSize);
Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_ALPHA_SIZE: {0}", alphaSize);
Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_BUFFER_SIZE: {0}", bufferSize);
Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_DOUBLEBUFFER: {0}", doubleBuffer);
Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_DEPTH_SIZE: {0}", depthSize);
Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_STENCIL_SIZE: {0}", stencilSize);
Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_ACCUM_RED_SIZE: {0}", accumRedSize);
Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_ACCUM_GREEN_SIZE: {0}", accumGreenSize);
Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_ACCUM_BLUE_SIZE: {0}", accumBlueSize);
Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_ACCUM_ALPHA_SIZE: {0}", accumAlphaSize);
Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_STEREO: {0}", stereo);
Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_MULTISAMPLEBUFFERS: {0}", multisampleBuffers);
Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_MULTISAMPLESAMPLES: {0}", multisampleSamples);
Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_ACCELERATED_VISUAL: {0}", acceleratedVisual);
Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_CONTEXT_MAJOR_VERSION: {0}", contextMajorVersion);
Platform.Services.Logger.Info(LOOPER_TAG, "\tGL_CONTEXT_MINOR_VERSION: {0}", contextMinorVersion);
Platform.Services.Logger.Info(LOOPER_TAG, "Attempting to enable V-sync.");
if (SDL.SDL_GL_SetSwapInterval(1) != 0)
Platform.Services.Logger.Warn(LOOPER_TAG, "Could not set swap interval: {0}", SDL.SDL_GetError());
else
Platform.Services.Logger.Info(LOOPER_TAG, "Swap interval set successful.");
return true;
}
private bool DestroyOpenGLContext()
{
Platform.Services.Logger.Info(LOOPER_TAG, "Destroying OpenGL context.");
if (_glContext == IntPtr.Zero)
{
Platform.Services.Logger.Warn(LOOPER_TAG, "No OpenGL context currently exists, not doing anything.");
return true;
}
SDL.SDL_GL_DeleteContext(_glContext);
_glContext = IntPtr.Zero;
Platform.Services.Logger.Info(LOOPER_TAG, "OpenGL context destroyed.");
return true;
}
#endregion
#region Events
private void DoEvents()
{
SDL.SDL_Event e;
while (SDL.SDL_PollEvent(out e) != 0)
{
if (e.type == SDL.SDL_EventType.SDL_WINDOWEVENT)
{
switch (e.window.windowEvent)
{
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_MINIMIZED:
Platform.Services.Logger.Info(LOOPER_TAG, "Window focus lost.");
Platform.Services.Logger.Info(LOOPER_TAG, "Window marked inactive.");
break;
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_RESTORED:
Platform.Services.Logger.Info(LOOPER_TAG, "Window focus gained.");
Platform.Services.Logger.Info(LOOPER_TAG, "Window marked active.");
break;
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_ENTER:
Platform.Services.Logger.Info(LOOPER_TAG, "Gained mouse focus.");
break;
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_LEAVE:
Platform.Services.Logger.Info(LOOPER_TAG, "Lost mouse focus.");
break;
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_GAINED:
Platform.Services.Logger.Info(LOOPER_TAG, "Gained input device focus.");
OnAppGainFocus();
break;
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_LOST:
Platform.Services.Logger.Info(LOOPER_TAG, "Lost input device focus.");
OnAppLostFocus();
break;
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_RESIZED:
Platform.Services.Logger.Info(LOOPER_TAG, "Window resized to {0}x{1}.", e.window.data1, e.window.data2);
Rect size = new Rect();
size.Right = e.window.data1;
size.Bottom = e.window.data2;
SetWindowInfo();
OnResize(ScreenOrientation.Rotation0, size);
break;
}
}
else
{
switch (e.type)
{
case SDL.SDL_EventType.SDL_QUIT:
Platform.Services.Logger.Info(LOOPER_TAG, "Event: SQL_QUIT");
_isQuitting = true;
break;
case SDL.SDL_EventType.SDL_KEYDOWN:
case SDL.SDL_EventType.SDL_KEYUP:
_keyboard.OnKeyEvent(e.key);
break;
case SDL.SDL_EventType.SDL_MOUSEMOTION:
_mouse.OnMotionEvent(e.motion);
break;
case SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN:
case SDL.SDL_EventType.SDL_MOUSEBUTTONUP:
_mouse.OnButtonEvent(e.button);
break;
}
}
}
}
#endregion
#region IDisposable
private void ReleaseSDL()
@ -86,20 +580,14 @@ namespace Blarg.GameFramework
if (!_isSDLinited)
return;
if (_glContext != IntPtr.Zero)
{
SDL.SDL_GL_DeleteContext(_glContext);
_glContext = IntPtr.Zero;
}
if (_window != IntPtr.Zero)
{
SDL.SDL_DestroyWindow(_window);
_window = IntPtr.Zero;
}
Platform.Services.Logger.Info(LOOPER_TAG, "Releasing SDL.");
DestroyOpenGLContext();
DestroyWindow();
SDL.SDL_Quit();
_isSDLinited = false;
Platform.Services.Logger.Info("Looper", "SDL shutdown.");
Platform.Services.Logger.Info(LOOPER_TAG, "SDL shutdown.");
}
~SDLLooper()
@ -107,7 +595,7 @@ namespace Blarg.GameFramework
ReleaseSDL();
}
public void Dispose()
public override void Dispose()
{
ReleaseSDL();
GC.SuppressFinalize(this);

View file

@ -0,0 +1,38 @@
using System;
using PortableGL;
using Blarg.GameFramework.Input;
using Blarg.GameFramework.IO;
namespace Blarg.GameFramework
{
public class SDLPlatformServices : IPlatformServices
{
public SDLPlatformServices()
{
if (CurrentOS.IsWindows)
OperatingSystem = PlatformOS.Windows;
else if (CurrentOS.IsLinux)
OperatingSystem = PlatformOS.Linux;
else if (CurrentOS.IsMac)
OperatingSystem = PlatformOS.MacOS;
else
throw new Exception("Unable to determine OS.");
Logger = new SDLLogger();
}
public PlatformOS OperatingSystem { get; private set; }
public PlatformType Type
{
get { return PlatformType.Desktop; }
}
public IPlatformLogger Logger { get; internal set; }
public IFileSystem FileSystem { get; internal set; }
public IKeyboard Keyboard { get; internal set; }
public IMouse Mouse { get; internal set; }
public ITouchScreen TouchScreen { get; internal set; }
public GL20 GL { get; internal set; }
}
}

View file

@ -0,0 +1,16 @@
using System;
namespace Blarg.GameFramework
{
public class SDLWindow : IPlatformWindow
{
public Rect ClientRectangle { get; internal set; }
public int ClientWidth { get; internal set; }
public int ClientHeight { get; internal set; }
public int X { get; internal set; }
public int Y { get; internal set; }
public int Width { get; internal set; }
public int Height { get; internal set; }
}
}

View file

@ -0,0 +1,91 @@
using System;
using Blarg.GameFramework.Graphics;
namespace Blarg.GameFramework
{
public abstract class BaseLooper : ILooper
{
IGameApp _gameApp;
public IGameApp GameApp
{
get
{
return _gameApp;
}
protected set
{
_gameApp = value;
}
}
public abstract int FPS { get; }
public abstract float FrameTime { get; }
public abstract int RendersPerSecond { get; }
public abstract int UpdatesPerSecond { get; }
public abstract int RenderTime { get; }
public abstract int UpdateTime { get; }
public abstract void Run(IGameApp gameApp, IPlatformConfiguration config);
protected void OnAppGainFocus()
{
GameApp.OnAppGainFocus();
}
protected void OnAppLostFocus()
{
GameApp.OnAppLostFocus();
}
protected void OnAppPause()
{
GameApp.OnAppPause();
}
protected void OnAppResume()
{
GameApp.OnAppResume();
}
protected void OnLoad()
{
GameApp.OnLoad();
}
protected void OnUnload()
{
GameApp.OnUnload();
}
protected void OnLostContext()
{
GameApp.OnLostContext();
}
protected void OnNewContext()
{
GameApp.OnNewContext();
}
protected void OnRender(float delta)
{
GameApp.OnRender(delta);
}
protected void OnResize(ScreenOrientation orientation, Rect size)
{
GameApp.OnResize(orientation, size);
}
protected void OnUpdate(float delta)
{
GameApp.OnUpdate(delta);
}
public virtual void Dispose()
{
}
}
}

View file

@ -82,6 +82,9 @@
<Compile Include="Graphics\Image.cs" />
<Compile Include="Graphics\ScreenOrientation.cs" />
<Compile Include="IGameApp.cs" />
<Compile Include="BaseLooper.cs" />
<Compile Include="IPlatformWindow.cs" />
<Compile Include="IPlatformConfiguration.cs" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<ItemGroup>

View file

@ -3,13 +3,12 @@ using Blarg.GameFramework.Graphics;
namespace Blarg.GameFramework
{
public interface IGameApp
public interface IGameApp : IDisposable
{
void OnAppGainFocus();
void OnAppLostFocus();
void OnAppPause();
void OnAppResume();
bool OnInit();
void OnLoad();
void OnUnload();
void OnLostContext();

View file

@ -4,7 +4,14 @@ namespace Blarg.GameFramework
{
public interface ILooper : IDisposable
{
void Run(IGameApp gameApp);
int FPS { get; }
float FrameTime { get; }
int RendersPerSecond { get; }
int UpdatesPerSecond { get; }
int RenderTime { get; }
int UpdateTime { get; }
void Run(IGameApp gameApp, IPlatformConfiguration config);
}
}

View file

@ -0,0 +1,9 @@
using System;
namespace Blarg.GameFramework
{
public interface IPlatformConfiguration
{
}
}

View file

@ -0,0 +1,16 @@
using System;
namespace Blarg.GameFramework
{
public interface IPlatformWindow
{
Rect ClientRectangle { get; }
int ClientWidth { get; }
int ClientHeight { get; }
int X { get; }
int Y { get; }
int Width { get; }
int Height { get; }
}
}

View file

@ -17,9 +17,21 @@ namespace Blarg.GameFramework
Desktop
}
public static partial class Platform
public static class Platform
{
public static IPlatformServices Services { get; private set; }
static IPlatformServices _services;
public static IPlatformServices Services
{
get { return _services; }
set
{
if (_services != null)
throw new InvalidOperationException();
else
_services = value;
}
}
}
}

View file

@ -35,9 +35,13 @@
<Reference Include="System" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="PortableGL">
<HintPath>..\Libs\PortableGL.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="GameApp.cs" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<ItemGroup>

65
Game.Core/GameApp.cs Normal file
View file

@ -0,0 +1,65 @@
using System;
using PortableGL;
using Blarg.GameFramework;
using Blarg.GameFramework.Graphics;
namespace Game
{
public class GameApp : IGameApp
{
public GameApp()
{
}
public void OnAppGainFocus()
{
}
public void OnAppLostFocus()
{
}
public void OnAppPause()
{
}
public void OnAppResume()
{
}
public void OnLoad()
{
}
public void OnUnload()
{
}
public void OnLostContext()
{
}
public void OnNewContext()
{
}
public void OnRender(float delta)
{
Platform.Services.GL.glClear(GL20.GL_DEPTH_BUFFER_BIT | GL20.GL_COLOR_BUFFER_BIT);
Platform.Services.GL.glClearColor(0.25f, 0.5f, 1.0f, 1.0f);
}
public void OnResize(ScreenOrientation orientation, Rect size)
{
}
public void OnUpdate(float delta)
{
}
public void Dispose()
{
}
}
}

View file

@ -6,7 +6,7 @@
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{4C73802F-6EB0-490C-BB32-64718B0FEEBA}</ProjectGuid>
<OutputType>Exe</OutputType>
<OutputType>WinExe</OutputType>
<RootNamespace>Game</RootNamespace>
<AssemblyName>Game.SDL2</AssemblyName>
</PropertyGroup>
@ -18,7 +18,7 @@
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Externalconsole>true</Externalconsole>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>full</DebugType>

View file

@ -1,12 +1,18 @@
using System;
using Blarg.GameFramework;
namespace Game.SDL2
{
class MainClass
public static class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var game = new GameApp();
var config = new SDLConfiguration();
config.Title = "Test Game";
var looper = new SDLLooper();
looper.Run(game, config);
}
}
}