Created
September 30, 2019 10:31
-
-
Save MnemonicWME/0f0e629cc43c9a3ca40f0f02275e8e45 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#region File Description | |
//----------------------------------------------------------------------------- | |
// GraphicsDeviceControl.cs | |
// | |
// Microsoft XNA Community Game Platform | |
// Copyright (C) Microsoft Corporation. All rights reserved. | |
//----------------------------------------------------------------------------- | |
#endregion | |
#region Using Statements | |
using System; | |
using System.Drawing; | |
using System.Windows.Forms; | |
using Microsoft.Xna.Framework.Graphics; | |
#endregion | |
namespace DeadCode.Slate.Forms.Controls | |
{ | |
// System.Drawing and the XNA Framework both define Color and Rectangle | |
// types. To avoid conflicts, we specify exactly which ones to use. | |
using Color = System.Drawing.Color; | |
using Rectangle = Microsoft.Xna.Framework.Rectangle; | |
/// <summary> | |
/// Custom control uses the XNA Framework GraphicsDevice to render onto | |
/// a Windows Form. Derived classes can override the Initialize and Draw | |
/// methods to add their own drawing code. | |
/// </summary> | |
abstract public class GraphicsDeviceControl : UserControl | |
{ | |
#region Fields | |
// However many GraphicsDeviceControl instances you have, they all share | |
// the same underlying GraphicsDevice, managed by this helper service. | |
GraphicsDeviceService graphicsDeviceService; | |
#endregion | |
#region Properties | |
/// <summary> | |
/// Gets a GraphicsDevice that can be used to draw onto this control. | |
/// </summary> | |
public GraphicsDevice GraphicsDevice | |
{ | |
get { return graphicsDeviceService.GraphicsDevice; } | |
} | |
public string SearchPath { get; set; } = string.Empty; | |
/// <summary> | |
/// Gets an IServiceProvider containing our IGraphicsDeviceService. | |
/// This can be used with components such as the ContentManager, | |
/// which use this service to look up the GraphicsDevice. | |
/// </summary> | |
public ServiceContainer Services | |
{ | |
get { return services; } | |
} | |
ServiceContainer services = new ServiceContainer(); | |
#endregion | |
#region Initialization | |
/// <summary> | |
/// Initializes the control. | |
/// </summary> | |
protected override void OnCreateControl() | |
{ | |
// Don't initialize the graphics device if we are running in the designer. | |
if (!DesignMode) | |
{ | |
graphicsDeviceService = GraphicsDeviceService.AddRef(Handle, | |
ClientSize.Width, | |
ClientSize.Height); | |
// Register the service, so components like ContentManager can find it. | |
services.AddService<IGraphicsDeviceService>(graphicsDeviceService); | |
// Give derived classes a chance to initialize themselves. | |
Initialize(SearchPath); | |
} | |
base.OnCreateControl(); | |
} | |
/// <summary> | |
/// Disposes the control. | |
/// </summary> | |
protected override void Dispose(bool disposing) | |
{ | |
if (graphicsDeviceService != null) | |
{ | |
graphicsDeviceService.Release(disposing); | |
graphicsDeviceService = null; | |
} | |
base.Dispose(disposing); | |
} | |
#endregion | |
#region Paint | |
/// <summary> | |
/// Redraws the control in response to a WinForms paint message. | |
/// </summary> | |
protected override void OnPaint(PaintEventArgs e) | |
{ | |
string beginDrawError = BeginDraw(); | |
if (string.IsNullOrEmpty(beginDrawError)) | |
{ | |
// Draw the control using the GraphicsDevice. | |
Draw(); | |
EndDraw(); | |
} | |
else | |
{ | |
// If BeginDraw failed, show an error message using System.Drawing. | |
PaintUsingSystemDrawing(e.Graphics, beginDrawError); | |
} | |
} | |
/// <summary> | |
/// Attempts to begin drawing the control. Returns an error message string | |
/// if this was not possible, which can happen if the graphics device is | |
/// lost, or if we are running inside the Form designer. | |
/// </summary> | |
string BeginDraw() | |
{ | |
// If we have no graphics device, we must be running in the designer. | |
if (graphicsDeviceService == null) | |
{ | |
return Text + "\n\n" + GetType(); | |
} | |
// Make sure the graphics device is big enough, and is not lost. | |
string deviceResetError = HandleDeviceReset(); | |
if (!string.IsNullOrEmpty(deviceResetError)) | |
{ | |
return deviceResetError; | |
} | |
// Many GraphicsDeviceControl instances can be sharing the same | |
// GraphicsDevice. The device backbuffer will be resized to fit the | |
// largest of these controls. But what if we are currently drawing | |
// a smaller control? To avoid unwanted stretching, we set the | |
// viewport to only use the top left portion of the full backbuffer. | |
Viewport viewport = new Viewport(); | |
viewport.X = 0; | |
viewport.Y = 0; | |
viewport.Width = ClientSize.Width; | |
viewport.Height = ClientSize.Height; | |
viewport.MinDepth = 0; | |
viewport.MaxDepth = 1; | |
GraphicsDevice.Viewport = viewport; | |
return null; | |
} | |
/// <summary> | |
/// Ends drawing the control. This is called after derived classes | |
/// have finished their Draw method, and is responsible for presenting | |
/// the finished image onto the screen, using the appropriate WinForms | |
/// control handle to make sure it shows up in the right place. | |
/// </summary> | |
void EndDraw() | |
{ | |
try | |
{ | |
/* | |
Rectangle sourceRectangle = new Rectangle(0, 0, ClientSize.Width, | |
ClientSize.Height); | |
GraphicsDevice.Present(sourceRectangle, null, this.Handle); | |
*/ | |
GraphicsDevice.Present(); | |
} | |
catch | |
{ | |
// Present might throw if the device became lost while we were | |
// drawing. The lost device will be handled by the next BeginDraw, | |
// so we just swallow the exception. | |
} | |
} | |
/// <summary> | |
/// Helper used by BeginDraw. This checks the graphics device status, | |
/// making sure it is big enough for drawing the current control, and | |
/// that the device is not lost. Returns an error string if the device | |
/// could not be reset. | |
/// </summary> | |
string HandleDeviceReset() | |
{ | |
bool deviceNeedsReset = false; | |
switch (GraphicsDevice.GraphicsDeviceStatus) | |
{ | |
case GraphicsDeviceStatus.Lost: | |
// If the graphics device is lost, we cannot use it at all. | |
return "Graphics device lost"; | |
case GraphicsDeviceStatus.NotReset: | |
// If device is in the not-reset state, we should try to reset it. | |
deviceNeedsReset = true; | |
break; | |
default: | |
// If the device state is ok, check whether it is big enough. | |
PresentationParameters pp = GraphicsDevice.PresentationParameters; | |
deviceNeedsReset = (ClientSize.Width != pp.BackBufferWidth) || | |
(ClientSize.Height != pp.BackBufferHeight); | |
break; | |
} | |
// Do we need to reset the device? | |
if (deviceNeedsReset) | |
{ | |
try | |
{ | |
graphicsDeviceService.ResetDevice(ClientSize.Width, | |
ClientSize.Height); | |
} | |
catch (Exception e) | |
{ | |
return "Graphics device reset failed\n\n" + e; | |
} | |
} | |
return null; | |
} | |
/// <summary> | |
/// If we do not have a valid graphics device (for instance if the device | |
/// is lost, or if we are running inside the Form designer), we must use | |
/// regular System.Drawing method to display a status message. | |
/// </summary> | |
protected virtual void PaintUsingSystemDrawing(Graphics graphics, string text) | |
{ | |
graphics.Clear(Color.CornflowerBlue); | |
using (Brush brush = new SolidBrush(Color.Black)) | |
{ | |
using (StringFormat format = new StringFormat()) | |
{ | |
format.Alignment = StringAlignment.Center; | |
format.LineAlignment = StringAlignment.Center; | |
graphics.DrawString(text, Font, brush, ClientRectangle, format); | |
} | |
} | |
} | |
/// <summary> | |
/// Ignores WinForms paint-background messages. The default implementation | |
/// would clear the control to the current background color, causing | |
/// flickering when our OnPaint implementation then immediately draws some | |
/// other color over the top using the XNA Framework GraphicsDevice. | |
/// </summary> | |
protected override void OnPaintBackground(PaintEventArgs pevent) | |
{ | |
} | |
#endregion | |
#region Abstract Methods | |
/// <summary> | |
/// Derived classes override this to initialize their drawing code. | |
/// </summary> | |
protected abstract void Initialize(string gameName); | |
/// <summary> | |
/// Derived classes override this to draw themselves using the GraphicsDevice. | |
/// </summary> | |
protected abstract void Draw(); | |
#endregion | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#region File Description | |
//----------------------------------------------------------------------------- | |
// GraphicsDeviceService.cs | |
// | |
// Microsoft XNA Community Game Platform | |
// Copyright (C) Microsoft Corporation. All rights reserved. | |
//----------------------------------------------------------------------------- | |
#endregion | |
#region Using Statements | |
using System; | |
using System.Threading; | |
using Microsoft.Xna.Framework.Graphics; | |
#endregion | |
// The IGraphicsDeviceService interface requires a DeviceCreated event, but we | |
// always just create the device inside our constructor, so we have no place to | |
// raise that event. The C# compiler warns us that the event is never used, but | |
// we don't care so we just disable this warning. | |
#pragma warning disable 67 | |
namespace DeadCode.Slate.Forms | |
{ | |
/// <summary> | |
/// Helper class responsible for creating and managing the GraphicsDevice. | |
/// All GraphicsDeviceControl instances share the same GraphicsDeviceService, | |
/// so even though there can be many controls, there will only ever be a single | |
/// underlying GraphicsDevice. This implements the standard IGraphicsDeviceService | |
/// interface, which provides notification events for when the device is reset | |
/// or disposed. | |
/// </summary> | |
class GraphicsDeviceService : IGraphicsDeviceService | |
{ | |
#region Fields | |
// Singleton device service instance. | |
static GraphicsDeviceService singletonInstance; | |
// Keep track of how many controls are sharing the singletonInstance. | |
static int referenceCount; | |
#endregion | |
/// <summary> | |
/// Constructor is private, because this is a singleton class: | |
/// client controls should use the public AddRef method instead. | |
/// </summary> | |
GraphicsDeviceService(IntPtr windowHandle, int width, int height) | |
{ | |
parameters = new PresentationParameters(); | |
parameters.BackBufferWidth = Math.Max(width, 1); | |
parameters.BackBufferHeight = Math.Max(height, 1); | |
parameters.BackBufferFormat = SurfaceFormat.Color; | |
parameters.DepthStencilFormat = DepthFormat.None; | |
parameters.DeviceWindowHandle = windowHandle; | |
parameters.PresentationInterval = PresentInterval.Immediate; | |
parameters.RenderTargetUsage = RenderTargetUsage.PreserveContents; | |
parameters.IsFullScreen = false; | |
graphicsDevice = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, | |
GraphicsProfile.Reach, | |
parameters); | |
} | |
/// <summary> | |
/// Gets a reference to the singleton instance. | |
/// </summary> | |
public static GraphicsDeviceService AddRef(IntPtr windowHandle, | |
int width, int height) | |
{ | |
// Increment the "how many controls sharing the device" reference count. | |
if (Interlocked.Increment(ref referenceCount) == 1) | |
{ | |
// If this is the first control to start using the | |
// device, we must create the singleton instance. | |
singletonInstance = new GraphicsDeviceService(windowHandle, | |
width, height); | |
} | |
return singletonInstance; | |
} | |
/// <summary> | |
/// Releases a reference to the singleton instance. | |
/// </summary> | |
public void Release(bool disposing) | |
{ | |
// Decrement the "how many controls sharing the device" reference count. | |
if (Interlocked.Decrement(ref referenceCount) == 0) | |
{ | |
// If this is the last control to finish using the | |
// device, we should dispose the singleton instance. | |
if (disposing) | |
{ | |
DeviceDisposing?.Invoke(this, EventArgs.Empty); | |
graphicsDevice.Dispose(); | |
} | |
graphicsDevice = null; | |
} | |
} | |
/// <summary> | |
/// Resets the graphics device to whichever is bigger out of the specified | |
/// resolution or its current size. This behavior means the device will | |
/// demand-grow to the largest of all its GraphicsDeviceControl clients. | |
/// </summary> | |
public void ResetDevice(int width, int height) | |
{ | |
DeviceResetting?.Invoke(this, EventArgs.Empty); | |
//parameters.BackBufferWidth = Math.Max(parameters.BackBufferWidth, width); | |
//parameters.BackBufferHeight = Math.Max(parameters.BackBufferHeight, height); | |
parameters.BackBufferWidth = width; | |
parameters.BackBufferHeight = height; | |
graphicsDevice.Reset(parameters); | |
DeviceReset?.Invoke(this, EventArgs.Empty); | |
} | |
/// <summary> | |
/// Gets the current graphics device. | |
/// </summary> | |
public GraphicsDevice GraphicsDevice | |
{ | |
get { return graphicsDevice; } | |
} | |
GraphicsDevice graphicsDevice; | |
// Store the current device settings. | |
PresentationParameters parameters; | |
// IGraphicsDeviceService events. | |
public event EventHandler<EventArgs> DeviceCreated; | |
public event EventHandler<EventArgs> DeviceDisposing; | |
public event EventHandler<EventArgs> DeviceReset; | |
public event EventHandler<EventArgs> DeviceResetting; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#region File Description | |
//----------------------------------------------------------------------------- | |
// ServiceContainer.cs | |
// | |
// Microsoft XNA Community Game Platform | |
// Copyright (C) Microsoft Corporation. All rights reserved. | |
//----------------------------------------------------------------------------- | |
#endregion | |
#region Using Statements | |
using System; | |
using System.Collections.Generic; | |
#endregion | |
namespace DeadCode.Slate.Forms | |
{ | |
/// <summary> | |
/// Container class implements the IServiceProvider interface. This is used | |
/// to pass shared services between different components, for instance the | |
/// ContentManager uses it to locate the IGraphicsDeviceService implementation. | |
/// </summary> | |
public class ServiceContainer : IServiceProvider | |
{ | |
Dictionary<Type, object> services = new Dictionary<Type, object>(); | |
/// <summary> | |
/// Adds a new service to the collection. | |
/// </summary> | |
public void AddService<T>(T service) | |
{ | |
services.Add(typeof(T), service); | |
} | |
/// <summary> | |
/// Looks up the specified service. | |
/// </summary> | |
public object GetService(Type serviceType) | |
{ | |
object service; | |
services.TryGetValue(serviceType, out service); | |
return service; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment