Add support for hardware cursors.
This commit is contained in:
138
OpenRA.Game/Graphics/HardwareCursor.cs
Normal file
138
OpenRA.Game/Graphics/HardwareCursor.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public class HardwareCursor : ICursor
|
||||
{
|
||||
readonly Dictionary<string, IHardwareCursor[]> hardwareCursors = new Dictionary<string, IHardwareCursor[]>();
|
||||
readonly CursorProvider cursorProvider;
|
||||
CursorSequence cursor;
|
||||
|
||||
public HardwareCursor(CursorProvider cursorProvider)
|
||||
{
|
||||
this.cursorProvider = cursorProvider;
|
||||
|
||||
foreach (var kv in cursorProvider.Cursors)
|
||||
{
|
||||
var palette = cursorProvider.Palettes[kv.Value.Palette];
|
||||
var hc = kv.Value.Frames
|
||||
.Select(f => CreateCursor(f, palette, kv.Key, kv.Value))
|
||||
.ToArray();
|
||||
|
||||
hardwareCursors.Add(kv.Key, hc);
|
||||
}
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
IHardwareCursor CreateCursor(ISpriteFrame f, ImmutablePalette palette, string name, CursorSequence sequence)
|
||||
{
|
||||
var hotspot = sequence.Hotspot - f.Offset.ToInt2() + new int2(f.Size) / 2;
|
||||
|
||||
// Expand the frame if required to include the hotspot
|
||||
var frameWidth = f.Size.Width;
|
||||
var dataWidth = f.Size.Width;
|
||||
var dataX = 0;
|
||||
if (hotspot.X < 0)
|
||||
{
|
||||
dataX = -hotspot.X;
|
||||
dataWidth += dataX;
|
||||
hotspot.X = 0;
|
||||
}
|
||||
else if (hotspot.X >= frameWidth)
|
||||
dataWidth = hotspot.X + 1;
|
||||
|
||||
var frameHeight = f.Size.Height;
|
||||
var dataHeight = f.Size.Height;
|
||||
var dataY = 0;
|
||||
if (hotspot.Y < 0)
|
||||
{
|
||||
dataY = -hotspot.Y;
|
||||
dataHeight += dataY;
|
||||
hotspot.Y = 0;
|
||||
}
|
||||
else if (hotspot.Y >= frameHeight)
|
||||
dataHeight = hotspot.Y + 1;
|
||||
|
||||
var data = new byte[4 * dataWidth * dataHeight];
|
||||
for (var j = 0; j < frameHeight; j++)
|
||||
{
|
||||
for (var i = 0; i < frameWidth; i++)
|
||||
{
|
||||
var bytes = BitConverter.GetBytes(palette[f.Data[j * frameWidth + i]]);
|
||||
var start = 4 * ((j + dataY) * dataWidth + dataX + i);
|
||||
for (var k = 0; k < 4; k++)
|
||||
data[start + k] = bytes[k];
|
||||
}
|
||||
}
|
||||
|
||||
return Game.Renderer.Device.CreateHardwareCursor(name, new Size(dataWidth, dataHeight), data, hotspot);
|
||||
}
|
||||
|
||||
public void SetCursor(string cursorName)
|
||||
{
|
||||
if ((cursorName == null && cursor == null) || (cursor != null && cursorName == cursor.Name))
|
||||
return;
|
||||
|
||||
if (cursorName == null || !cursorProvider.Cursors.TryGetValue(cursorName, out cursor))
|
||||
cursor = null;
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
int frame;
|
||||
int ticks;
|
||||
public void Tick()
|
||||
{
|
||||
if (cursor == null || cursor.Length == 1)
|
||||
return;
|
||||
|
||||
if (++ticks > 2)
|
||||
{
|
||||
ticks -= 2;
|
||||
frame++;
|
||||
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (cursor == null)
|
||||
Game.Renderer.Device.SetHardwareCursor(null);
|
||||
else
|
||||
{
|
||||
if (frame >= cursor.Length)
|
||||
frame = frame % cursor.Length;
|
||||
|
||||
Game.Renderer.Device.SetHardwareCursor(hardwareCursors[cursor.Name][frame]);
|
||||
}
|
||||
}
|
||||
|
||||
public void Render(Renderer renderer) { }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var cursors in hardwareCursors)
|
||||
foreach (var cursor in cursors.Value)
|
||||
cursor.Dispose();
|
||||
|
||||
hardwareCursors.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,8 @@ namespace OpenRA
|
||||
IGraphicsDevice Create(Size size, WindowMode windowMode);
|
||||
}
|
||||
|
||||
public interface IHardwareCursor : IDisposable { }
|
||||
|
||||
public enum BlendMode : byte { None, Alpha, Additive, Subtractive, Multiply }
|
||||
|
||||
public interface IGraphicsDevice : IDisposable
|
||||
@@ -61,6 +63,9 @@ namespace OpenRA
|
||||
|
||||
void GrabWindowMouseFocus();
|
||||
void ReleaseWindowMouseFocus();
|
||||
|
||||
IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot);
|
||||
void SetHardwareCursor(IHardwareCursor cursor);
|
||||
}
|
||||
|
||||
public interface IVertexBuffer<T> : IDisposable
|
||||
|
||||
@@ -50,6 +50,8 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
|
||||
sheetBuilder.Current.ReleaseBuffer();
|
||||
|
||||
Game.Renderer.Device.SetHardwareCursor(null);
|
||||
}
|
||||
|
||||
PaletteReference CreatePaletteReference(string name)
|
||||
|
||||
@@ -253,6 +253,7 @@
|
||||
<Compile Include="Graphics\UISpriteRenderable.cs" />
|
||||
<Compile Include="GameRules\DamageWarhead.cs" />
|
||||
<Compile Include="Graphics\SoftwareCursor.cs" />
|
||||
<Compile Include="Graphics\HardwareCursor.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="FileSystem\D2kSoundResources.cs" />
|
||||
|
||||
@@ -66,6 +66,9 @@ namespace OpenRA.Renderer.Null
|
||||
public ITexture CreateTexture(Bitmap bitmap) { return new NullTexture(); }
|
||||
public IFrameBuffer CreateFrameBuffer(Size s) { return new NullFrameBuffer(); }
|
||||
public IShader CreateShader(string name) { return new NullShader(); }
|
||||
|
||||
public IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot) { return null; }
|
||||
public void SetHardwareCursor(IHardwareCursor cursor) { }
|
||||
}
|
||||
|
||||
public class NullShader : IShader
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using OpenRA;
|
||||
using OpenRA.Graphics;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
@@ -80,7 +82,6 @@ namespace OpenRA.Renderer.Sdl2
|
||||
SDL.SDL_SetHint(SDL.SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
|
||||
}
|
||||
|
||||
SDL.SDL_ShowCursor(0);
|
||||
context = SDL.SDL_GL_CreateContext(window);
|
||||
SDL.SDL_GL_MakeCurrent(window, context);
|
||||
GL.LoadAll();
|
||||
@@ -103,10 +104,53 @@ namespace OpenRA.Renderer.Sdl2
|
||||
input = new Sdl2Input();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public void SetHardwareCursor(IHardwareCursor cursor)
|
||||
{
|
||||
var c = cursor as SDL2HardwareCursor;
|
||||
if (c == null)
|
||||
SDL.SDL_ShowCursor(0);
|
||||
else
|
||||
{
|
||||
SDL.SDL_ShowCursor(1);
|
||||
SDL.SDL_SetCursor(c.Cursor);
|
||||
}
|
||||
}
|
||||
|
||||
class SDL2HardwareCursor : IHardwareCursor
|
||||
{
|
||||
public readonly IntPtr Cursor;
|
||||
readonly IntPtr surface;
|
||||
|
||||
public SDL2HardwareCursor(Size size, byte[] data, int2 hotspot)
|
||||
{
|
||||
surface = SDL.SDL_CreateRGBSurface(0, size.Width, size.Height, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
SDL.SDL_FreeCursor(Cursor);
|
||||
SDL.SDL_FreeSurface(surface);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
disposed = true;
|
||||
if (context != IntPtr.Zero)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user