Prevent leaking of hardware cursors.

Added the lacking finialization machinery, ensured disposal under exceptional circumstances and also ensure the game calls dispose on old cursors before creating new ones.
This commit is contained in:
RoosterDragon
2015-04-21 20:24:26 +01:00
parent b4fa704fbd
commit c963806dae
2 changed files with 53 additions and 14 deletions

View File

@@ -309,6 +309,9 @@ namespace OpenRA
using (new PerfTimer("LoadMaps"))
ModData.MapCache.LoadMaps();
if (Cursor != null)
Cursor.Dispose();
if (Settings.Graphics.HardwareCursors)
{
try

View File

@@ -106,11 +106,14 @@ namespace OpenRA.Renderer.Sdl2
public IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot)
{
var c = new SDL2HardwareCursor(size, data, hotspot);
if (c.Cursor == IntPtr.Zero)
throw new InvalidDataException("Failed to create hardware cursor `{0}`: {1}".F(name, SDL.SDL_GetError()));
return c;
try
{
return new SDL2HardwareCursor(size, data, hotspot);
}
catch (Exception ex)
{
throw new InvalidDataException("Failed to create hardware cursor `{0}`".F(name), ex);
}
}
public void SetHardwareCursor(IHardwareCursor cursor)
@@ -125,24 +128,57 @@ namespace OpenRA.Renderer.Sdl2
}
}
class SDL2HardwareCursor : IHardwareCursor
sealed class SDL2HardwareCursor : IHardwareCursor
{
public readonly IntPtr Cursor;
readonly IntPtr surface;
public IntPtr Cursor { get; private set; }
IntPtr surface;
public SDL2HardwareCursor(Size size, byte[] data, int2 hotspot)
{
surface = SDL.SDL_CreateRGBSurface(0, size.Width, size.Height, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
try
{
surface = SDL.SDL_CreateRGBSurface(0, size.Width, size.Height, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
if (surface == IntPtr.Zero)
throw new InvalidDataException("Failed to create surface: {0}".F(SDL.SDL_GetError()));
var sur = (SDL2.SDL.SDL_Surface)Marshal.PtrToStructure(surface, typeof(SDL2.SDL.SDL_Surface));
Marshal.Copy(data, 0, sur.pixels, data.Length);
Cursor = SDL.SDL_CreateColorCursor(surface, hotspot.X, hotspot.Y);
var sur = (SDL2.SDL.SDL_Surface)Marshal.PtrToStructure(surface, typeof(SDL2.SDL.SDL_Surface));
Marshal.Copy(data, 0, sur.pixels, data.Length);
Cursor = SDL.SDL_CreateColorCursor(surface, hotspot.X, hotspot.Y);
if (Cursor == IntPtr.Zero)
throw new InvalidDataException("Failed to create cursor: {0}".F(SDL.SDL_GetError()));
}
catch
{
Dispose();
throw;
}
}
~SDL2HardwareCursor()
{
Game.RunAfterTick(() => Dispose(false));
}
public void Dispose()
{
SDL.SDL_FreeCursor(Cursor);
SDL.SDL_FreeSurface(surface);
Game.RunAfterTick(() => Dispose(true));
GC.SuppressFinalize(this);
}
void Dispose(bool disposing)
{
if (Cursor != IntPtr.Zero)
{
SDL.SDL_FreeCursor(Cursor);
Cursor = IntPtr.Zero;
}
if (surface != IntPtr.Zero)
{
SDL.SDL_FreeSurface(surface);
surface = IntPtr.Zero;
}
}
}