Merge branch 'cursor' of https://github.com/thefiddler/opentk into thefiddler-cursor

This commit is contained in:
thefiddler 2014-04-28 07:07:47 +02:00
commit 4f9a2f78d6
18 changed files with 919 additions and 13 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

@ -146,6 +146,7 @@
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="OpenGL\1.x\TextRendering.cs" /> <Compile Include="OpenGL\1.x\TextRendering.cs" />
<Compile Include="OpenTK\GameWindow\MouseCursorSimple.cs" />
<Compile Include="OpenTK\Test\TestShaderUtf8Support.cs" /> <Compile Include="OpenTK\Test\TestShaderUtf8Support.cs" />
<Compile Include="SamplesTreeViewSorter.cs"> <Compile Include="SamplesTreeViewSorter.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
@ -547,10 +548,14 @@
</None> </None>
<None Include="Data\Audio\the_ring_that_fell.wav"> <None Include="Data\Audio\the_ring_that_fell.wav">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Data\Textures\cursor.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<EmbeddedResource Include="OpenGL\1.x\OpenGLDiagnostics.rtf" /> <EmbeddedResource Include="OpenGL\1.x\OpenGLDiagnostics.rtf" />
<EmbeddedResource Include="OpenGL\1.x\Anaglyph.rtf" /> <EmbeddedResource Include="OpenGL\1.x\Anaglyph.rtf" />
<EmbeddedResource Include="OpenGL\1.x\TextRendering.rtf" /> <EmbeddedResource Include="OpenGL\1.x\TextRendering.rtf" />
<EmbeddedResource Include="OpenTK\GameWindow\MouseCursorSimple.rtf" />
<None Include="Resources\App.ico"> <None Include="Resources\App.ico">
</None> </None>
<None Include="..\OpenTK\OpenTK.dll.config"> <None Include="..\OpenTK\OpenTK.dll.config">

View file

@ -0,0 +1,171 @@
// This code was written for the OpenTK library and has been released
// to the Public Domain.
// It is provided "as is" without express or implied warranty of any kind.
using System;
using System.Drawing;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;
namespace Examples.Tutorial
{
/// <summary>
/// Demonstrates the MouseCursor class.
/// </summary>
[Example("MouseCursor Simple", ExampleCategory.OpenTK, "GameWindow", 1, Documentation = "MouseCursorSimple")]
public class MouseCursorSimple : GameWindow
{
public MouseCursorSimple()
: base(800, 600)
{
Keyboard.KeyDown += Keyboard_KeyDown;
Bitmap bitmap = new Bitmap("Data/Textures/cursor.png");
var rgba = new byte[bitmap.Width * bitmap.Height * 4];
var data = bitmap.LockBits(
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
for (int y = 0; y < bitmap.Height; ++y)
{
var offset = new IntPtr(data.Scan0.ToInt64() + (data.Stride * y));
var stride = bitmap.Width * 4;
System.Runtime.InteropServices.Marshal.Copy(
offset, rgba, y * stride, stride);
}
//this.Cursor = new OpenTK.MouseCursor(rgba, bitmap.Width, bitmap.Height, 0, 0);
this.Cursor = MouseCursor.Default;
}
#region Keyboard_KeyDown
/// <summary>
/// Occurs when a key is pressed.
/// </summary>
/// <param name="sender">The KeyboardDevice which generated this event.</param>
/// <param name="e">The key that was pressed.</param>
void Keyboard_KeyDown(object sender, KeyboardKeyEventArgs e)
{
if (e.Key == Key.Escape)
{
this.Exit();
}
if (e.Key == Key.Enter && e.Alt)
{
if (this.WindowState == WindowState.Fullscreen)
this.WindowState = WindowState.Normal;
else
this.WindowState = WindowState.Fullscreen;
}
if (e.Key == Key.Space)
{
if (Cursor == MouseCursor.Default)
{
Cursor = MouseCursor.Empty;
}
else
{
Cursor = MouseCursor.Default;
}
}
}
#endregion
#region OnLoad
/// <summary>
/// Setup OpenGL and load resources here.
/// </summary>
/// <param name="e">Not used.</param>
protected override void OnLoad(EventArgs e)
{
GL.ClearColor(Color.MidnightBlue);
}
#endregion
#region OnResize
/// <summary>
/// Respond to resize events here.
/// </summary>
/// <param name="e">Contains information on the new GameWindow size.</param>
/// <remarks>There is no need to call the base implementation.</remarks>
protected override void OnResize(EventArgs e)
{
GL.Viewport(0, 0, Width, Height);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Ortho(-1.0, 1.0, -1.0, 1.0, 0.0, 4.0);
}
#endregion
#region OnUpdateFrame
/// <summary>
/// Add your game logic here.
/// </summary>
/// <param name="e">Contains timing information.</param>
/// <remarks>There is no need to call the base implementation.</remarks>
protected override void OnUpdateFrame(FrameEventArgs e)
{
// Nothing to do!
}
#endregion
#region OnRenderFrame
/// <summary>
/// Add your game rendering code here.
/// </summary>
/// <param name="e">Contains timing information.</param>
/// <remarks>There is no need to call the base implementation.</remarks>
protected override void OnRenderFrame(FrameEventArgs e)
{
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.Begin(PrimitiveType.Triangles);
GL.Color3(Color.MidnightBlue);
GL.Vertex2(-1.0f, 1.0f);
GL.Color3(Color.SpringGreen);
GL.Vertex2(0.0f, -1.0f);
GL.Color3(Color.Ivory);
GL.Vertex2(1.0f, 1.0f);
GL.End();
this.SwapBuffers();
}
#endregion
#region public static void Main()
/// <summary>
/// Entry point of this example.
/// </summary>
[STAThread]
public static void Main()
{
using (MouseCursorSimple example = new MouseCursorSimple())
{
// Get the title and category of this example using reflection.
Utilities.SetWindowTitle(example);
example.Run(30.0, 0.0);
}
}
#endregion
}
}

View file

@ -132,6 +132,12 @@ namespace OpenTK
[Obsolete("Use OpenTK.Input.Mouse/Keybord/Joystick/GamePad instead.")] [Obsolete("Use OpenTK.Input.Mouse/Keybord/Joystick/GamePad instead.")]
OpenTK.Input.IInputDriver InputDriver { get; } OpenTK.Input.IInputDriver InputDriver { get; }
/// <summary>
/// Gets or sets the <see cref="OpenTK.MouseCursor"/> for this window.
/// </summary>
/// <value>The cursor.</value>
MouseCursor Cursor { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value, indicating whether the mouse cursor is visible. /// Gets or sets a value, indicating whether the mouse cursor is visible.
/// </summary> /// </summary>

View file

@ -211,12 +211,14 @@ namespace OpenTK.Input
{ {
args.Key = key; args.Key = key;
args.ScanCode = scancode; args.ScanCode = scancode;
args.Modifiers = GetModifiers();
KeyDown(this, args); KeyDown(this, args);
} }
else if (!state && KeyUp != null) else if (!state && KeyUp != null)
{ {
args.Key = key; args.Key = key;
args.ScanCode = scancode; args.ScanCode = scancode;
args.Modifiers = GetModifiers();
KeyUp(this, args); KeyUp(this, args);
} }
} }

View file

@ -0,0 +1,101 @@
#region License
//
// Cursor.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-2014 Stefanos Apostolopoulos
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#endregion
using System;
namespace OpenTK
{
/// <summary>
/// Represents a predefined or custom mouse cursor.
/// </summary>
public sealed class MouseCursor : WindowIcon
{
static readonly MouseCursor default_cursor = new MouseCursor();
static readonly MouseCursor empty_cursor = new MouseCursor(
new byte[16 * 16 * 4], 16, 16, 0, 0);
byte[] rgba;
int width;
int height;
int x;
int y;
MouseCursor()
{
}
// Todo: make public when byte-order issues are resolved
internal MouseCursor(byte[] rgba, int width, int height, int x, int y)
{
if (rgba == null)
throw new ArgumentNullException();
if (width < 0 || width > 256 || height < 0 || height > 256)
throw new ArgumentOutOfRangeException();
if (rgba.Length < width * height * 4)
throw new ArgumentOutOfRangeException();
if (x < 0 || x >= width || y < 0 || y >= height)
throw new ArgumentOutOfRangeException();
this.rgba = rgba;
this.width = width;
this.height = height;
this.x = x;
this.y = y;
}
internal byte[] Rgba { get { return rgba; } }
internal int Width { get { return width; } }
internal int Height { get { return height; } }
internal int X { get { return x; } }
internal int Y { get { return y; } }
/// <summary>
/// Gets the default mouse cursor for this platform.
/// </summary>
public static MouseCursor Default
{
get
{
return default_cursor;
}
}
/// <summary>
/// Gets an empty (invisible) mouse cursor.
/// </summary>
public static MouseCursor Empty
{
get
{
return empty_cursor;
}
}
}
}

View file

@ -259,6 +259,31 @@ namespace OpenTK
#endregion #endregion
#region Cursor
/// <summary>
/// Gets or sets the <see cref="OpenTK.MouseCursor"/> for this window.
/// </summary>
public MouseCursor Cursor
{
get
{
EnsureUndisposed();
return implementation.Cursor;
}
set
{
EnsureUndisposed();
if (value == null)
{
value = MouseCursor.Empty;
}
implementation.Cursor = value;
}
}
#endregion
#region Exists #region Exists
/// <summary> /// <summary>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup> <PropertyGroup>
<ProjectType>Local</ProjectType> <ProjectType>Local</ProjectType>
@ -759,6 +759,7 @@
<Compile Include="Input\JoystickHat.cs" /> <Compile Include="Input\JoystickHat.cs" />
<Compile Include="Input\HatPosition.cs" /> <Compile Include="Input\HatPosition.cs" />
<Compile Include="Input\JoystickHatState.cs" /> <Compile Include="Input\JoystickHatState.cs" />
<Compile Include="MouseCursor.cs" />
<Compile Include="Input\KeyModifiers.cs" /> <Compile Include="Input\KeyModifiers.cs" />
<Compile Include="Platform\MacOS\CocoaContext.cs" /> <Compile Include="Platform\MacOS\CocoaContext.cs" />
<Compile Include="Platform\MacOS\CocoaNativeWindow.cs" /> <Compile Include="Platform\MacOS\CocoaNativeWindow.cs" />
@ -794,6 +795,7 @@
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="Platform\MacOS\Carbon\Cgl.cs" /> <Compile Include="Platform\MacOS\Carbon\Cgl.cs" />
<Compile Include="WindowIcon.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>
@ -823,5 +825,4 @@
</Properties> </Properties>
</MonoDevelop> </MonoDevelop>
</ProjectExtensions> </ProjectExtensions>
<ItemGroup />
</Project> </Project>

View file

@ -83,6 +83,8 @@ namespace OpenTK.Platform.MacOS
float mouse_rel_x; float mouse_rel_x;
float mouse_rel_y; float mouse_rel_y;
MouseCursor cursor = MouseCursor.Default;
#endregion #endregion
#region AGL Device Hack #region AGL Device Hack
@ -935,6 +937,18 @@ namespace OpenTK.Platform.MacOS
} }
} }
public MouseCursor Cursor
{
get
{
return cursor;
}
set
{
Debug.Print("[Warning] CarbonGLNative.Cursor property not implemented");
}
}
public bool CursorVisible public bool CursorVisible
{ {
get { return CG.CursorIsVisible(); } get { return CG.CursorIsVisible(); }

View file

@ -112,6 +112,7 @@ namespace OpenTK.Platform.MacOS
//static readonly IntPtr selIsInFullScreenMode = Selector.Get("isInFullScreenMode"); //static readonly IntPtr selIsInFullScreenMode = Selector.Get("isInFullScreenMode");
//static readonly IntPtr selExitFullScreenModeWithOptions = Selector.Get("exitFullScreenModeWithOptions:"); //static readonly IntPtr selExitFullScreenModeWithOptions = Selector.Get("exitFullScreenModeWithOptions:");
//static readonly IntPtr selEnterFullScreenModeWithOptions = Selector.Get("enterFullScreenMode:withOptions:"); //static readonly IntPtr selEnterFullScreenModeWithOptions = Selector.Get("enterFullScreenMode:withOptions:");
static readonly IntPtr selArrowCursor = Selector.Get("arrowCursor");
static readonly IntPtr NSDefaultRunLoopMode; static readonly IntPtr NSDefaultRunLoopMode;
static readonly IntPtr NSCursor; static readonly IntPtr NSCursor;
@ -142,8 +143,10 @@ namespace OpenTK.Platform.MacOS
private int normalLevel; private int normalLevel;
private bool shouldClose; private bool shouldClose;
private int suppressResize; private int suppressResize;
private const float scrollFactor = 120.0f; private bool cursorInsideWindow = true;
private MouseCursor selectedCursor = MouseCursor.Default; // user-selected cursor
private const float scrollFactor = 120.0f;
private const bool exclusiveFullscreen = false; private const bool exclusiveFullscreen = false;
public CocoaNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) public CocoaNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device)
@ -412,11 +415,12 @@ namespace OpenTK.Platform.MacOS
var trackingAreaOwner = Cocoa.SendIntPtr(eventTrackingArea, selOwner); var trackingAreaOwner = Cocoa.SendIntPtr(eventTrackingArea, selOwner);
if (trackingAreaOwner == windowInfo.ViewHandle) if (trackingAreaOwner == windowInfo.ViewHandle)
{ {
if (!cursorVisible) if (selectedCursor != MouseCursor.Default)
{ {
SetCursorVisible(false); SetCursor(selectedCursor);
} }
cursorInsideWindow = true;
MouseEnter(this, EventArgs.Empty); MouseEnter(this, EventArgs.Empty);
} }
} }
@ -428,11 +432,12 @@ namespace OpenTK.Platform.MacOS
var trackingAreaOwner = Cocoa.SendIntPtr(eventTrackingArea, selOwner); var trackingAreaOwner = Cocoa.SendIntPtr(eventTrackingArea, selOwner);
if (trackingAreaOwner == windowInfo.ViewHandle) if (trackingAreaOwner == windowInfo.ViewHandle)
{ {
if (!cursorVisible) if (selectedCursor != MouseCursor.Default)
{ {
SetCursorVisible(true); SetCursor(MouseCursor.Default);
} }
cursorInsideWindow = false;
MouseLeave(this, EventArgs.Empty); MouseLeave(this, EventArgs.Empty);
} }
} }
@ -885,6 +890,27 @@ namespace OpenTK.Platform.MacOS
} }
} }
public MouseCursor Cursor
{
get
{
return selectedCursor;
}
set
{
// We only modify the cursor when it is
// inside the window and visible.
// If it is outside the window or invisible,
// we store the selected cursor and change it
// in the MouseEnter event.
if (CursorVisible && cursorInsideWindow)
{
SetCursor(value);
}
selectedCursor = value;
}
}
public bool CursorVisible public bool CursorVisible
{ {
get { return cursorVisible; } get { return cursorVisible; }
@ -966,11 +992,26 @@ namespace OpenTK.Platform.MacOS
private void SetCursorVisible(bool visible) private void SetCursorVisible(bool visible)
{ {
// Problem: Unlike the PC version, you can move the mouse out of the window. Carbon.CG.AssociateMouseAndMouseCursorPosition(visible);
// Perhaps use CG.WarpMouseCursorPosition to clamp mouse?
Cocoa.SendVoid(NSCursor, visible ? selUnhide : selHide); Cocoa.SendVoid(NSCursor, visible ? selUnhide : selHide);
} }
private void SetCursor(MouseCursor cursor)
{
if (cursor == MouseCursor.Default)
{
Cocoa.SendVoid(NSCursor, selUnhide);
}
else if (cursor == MouseCursor.Empty)
{
Cocoa.SendVoid(NSCursor, selHide);
}
else
{
throw new NotImplementedException();
}
}
private void SetMenuVisible(bool visible) private void SetMenuVisible(bool visible)
{ {
var options = (NSApplicationPresentationOptions)Cocoa.SendInt(NSApplication.Handle, selPresentationOptions); var options = (NSApplicationPresentationOptions)Cocoa.SendInt(NSApplication.Handle, selPresentationOptions);

View file

@ -33,6 +33,8 @@ using System.Runtime.InteropServices;
namespace OpenTK.Platform.SDL2 namespace OpenTK.Platform.SDL2
{ {
using Surface = IntPtr;
using Cursor = IntPtr;
partial class SDL partial class SDL
{ {
@ -77,6 +79,26 @@ namespace OpenTK.Platform.SDL2
// strlen++; // strlen++;
} }
#region Cursor
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_CreateColorCursor", ExactSpelling = true)]
public static extern Cursor CreateColorCursor(Surface surface, int hot_x, int hot_y);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_FreeCursor", ExactSpelling = true)]
public static extern void FreeCursor(Cursor cursor);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetDefaultCursor", ExactSpelling = true)]
public static extern IntPtr GetDefaultCursor();
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_SetCursor", ExactSpelling = true)]
public static extern void SetCursor(Cursor cursor);
#endregion
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_AddEventWatch", ExactSpelling = true)] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_AddEventWatch", ExactSpelling = true)]
public static extern void AddEventWatch(EventFilter filter, IntPtr userdata); public static extern void AddEventWatch(EventFilter filter, IntPtr userdata);
@ -220,6 +242,10 @@ namespace OpenTK.Platform.SDL2
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetModState", ExactSpelling = true)] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetModState", ExactSpelling = true)]
public static extern Keymod GetModState(); public static extern Keymod GetModState();
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetMouseState", ExactSpelling = true)]
public static extern ButtonFlags GetMouseState(out int hx, out int hy);
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetNumDisplayModes", ExactSpelling = true)] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetNumDisplayModes", ExactSpelling = true)]
public static extern int GetNumDisplayModes(int displayIndex); public static extern int GetNumDisplayModes(int displayIndex);

View file

@ -58,6 +58,8 @@ namespace OpenTK.Platform.SDL2
WindowState previous_window_state = WindowState.Normal; WindowState previous_window_state = WindowState.Normal;
WindowBorder window_border = WindowBorder.Resizable; WindowBorder window_border = WindowBorder.Resizable;
Icon icon; Icon icon;
MouseCursor cursor = MouseCursor.Default;
IntPtr sdl_cursor = IntPtr.Zero;
string window_title; string window_title;
// Used in KeyPress event to decode SDL UTF8 text strings // Used in KeyPress event to decode SDL UTF8 text strings
@ -458,6 +460,86 @@ namespace OpenTK.Platform.SDL2
public event EventHandler<EventArgs> MouseEnter = delegate { }; public event EventHandler<EventArgs> MouseEnter = delegate { };
public event EventHandler<EventArgs> MouseLeave = delegate { }; public event EventHandler<EventArgs> MouseLeave = delegate { };
public MouseCursor Cursor
{
get
{
return cursor;
}
set
{
lock (sync)
{
if (value != MouseCursor.Default)
{
// Free the previous cursor,
// if one has been set.
if (sdl_cursor != IntPtr.Zero)
{
SDL.FreeCursor(sdl_cursor);
sdl_cursor = IntPtr.Zero;
cursor = MouseCursor.Default;
}
// Set the new cursor
if (value == MouseCursor.Default)
{
// Reset to default cursor
SDL.SetCursor(SDL.GetDefaultCursor());
}
else
{
// Create and set a new cursor using
// the rgba values supplied by the user
unsafe
{
fixed (byte* pixels = value.Rgba)
{
IntPtr cursor_surface =
SDL.CreateRGBSurfaceFrom(
new IntPtr(pixels),
value.Width,
value.Height,
32,
value.Width * 4,
0xff000000,
0x00ff0000,
0x0000ff00,
0x000000ff);
if (cursor_surface == IntPtr.Zero)
{
Debug.Print("[SDL2] Failed to create cursor surface. Error: {0}",
SDL.GetError());
return;
}
sdl_cursor = SDL.CreateColorCursor(cursor_surface, value.X, value.Y);
if (sdl_cursor == IntPtr.Zero)
{
Debug.Print("[SDL2] Failed to create cursor. Error: {0}",
SDL.GetError());
return;
}
if (sdl_cursor != IntPtr.Zero)
{
SDL.SetCursor(sdl_cursor);
cursor = value;
}
if (cursor_surface != IntPtr.Zero)
{
SDL.FreeSurface(cursor_surface);
}
}
}
}
}
}
}
}
public void Close() public void Close()
{ {
lock (sync) lock (sync)

View file

@ -856,6 +856,98 @@ namespace OpenTK.Platform.Windows
#endregion #endregion
#region CreateIconIndirect
/// <summary>
/// Creates an icon or cursor from an IconInfo structure.
/// </summary>
/// <param name="iconInfo">
/// A pointer to an IconInfo structure the function uses to create the
/// icon or cursor.
/// </param>
/// <returns>
/// If the function succeeds, the return value is a handle to the icon
/// or cursor that is created.
///
/// If the function fails, the return value is null. To get extended
/// error information, call Marshal.GetLastWin32Error.
/// </returns>
/// <remarks>
/// The system copies the bitmaps in the IconInfo structure before
/// creating the icon or cursor. Because the system may temporarily
/// select the bitmaps in a device context, the hbmMask and hbmColor
/// members of the IconInfo structure should not already be selected
/// into a device context. The application must continue to manage the
/// original bitmaps and delete them when they are no longer necessary.
/// When you are finished using the icon, destroy it using the
/// DestroyIcon function.
/// </remarks>
[DllImport("user32.dll", SetLastError=true)]
public static extern HICON CreateIconIndirect(ref IconInfo iconInfo);
#endregion
#region GetIconInfo
/// <summary>
/// Retrieves information about the specified icon or cursor.
/// </summary>
/// <param name="hIcon">A handle to the icon or cursor.</param>
/// <param name="pIconInfo">
/// A pointer to an IconInfo structure. The function fills in the
/// structure's members.
/// </param>
/// <returns>
/// If the function succeeds, the return value is nonzero and the
/// function fills in the members of the specified IconInfo structure.
///
/// If the function fails, the return value is zero. To get extended
/// error information, call Marshal.GetLastWin32Error.
/// </returns>
/// <remarks>
/// GetIconInfo creates bitmaps for the hbmMask and hbmColor members
/// of IconInfo. The calling application must manage these bitmaps and
/// delete them when they are no longer necessary.
/// </remarks>
[DllImport("user32.dll", SetLastError=true)]
public static extern BOOL GetIconInfo(HICON hIcon, out IconInfo pIconInfo);
#endregion
#region DestroyIcon
/// <summary>
/// Destroys an icon and frees any memory the icon occupied.
/// </summary>
/// <param name="hIcon">
/// A handle to the icon to be destroyed. The icon must not be in use.
/// </param>
/// <returns>
/// If the function succeeds, the return value is nonzero.
///
/// If the function fails, the return value is zero. To get extended
/// error information, call Marshal.GetLastWin32Error.
/// </returns>
/// <remarks>
/// It is only necessary to call DestroyIcon for icons and cursors
/// created with the following functions: CreateIconFromResourceEx
/// (if called without the LR_SHARED flag), CreateIconIndirect, and
/// CopyIcon. Do not use this function to destroy a shared icon. A
/// shared icon is valid as long as the module from which it was loaded
/// remains in memory. The following functions obtain a shared icon.
///
/// LoadIcon
/// LoadImage (if you use the LR_SHARED flag)
/// CopyImage (if you use the LR_COPYRETURNORG flag and the hImage parameter is a shared icon)
/// CreateIconFromResource
/// CreateIconFromResourceEx (if you use the LR_SHARED flag)
/// </remarks>
[DllImport("user32.dll", SetLastError = true)]
public static extern BOOL DestroyIcon(HICON hIcon);
#endregion
[DllImport("user32.dll", SetLastError = true)] [DllImport("user32.dll", SetLastError = true)]
public static extern BOOL SetForegroundWindow(HWND hWnd); public static extern BOOL SetForegroundWindow(HWND hWnd);
@ -1043,6 +1135,53 @@ namespace OpenTK.Platform.Windows
uint cbSize, MouseMovePoint* pointsIn, uint cbSize, MouseMovePoint* pointsIn,
MouseMovePoint* pointsBufferOut, int nBufPoints, uint resolution); MouseMovePoint* pointsBufferOut, int nBufPoints, uint resolution);
/// <summary>
/// Sets the cursor shape.
/// </summary>
/// <param name="hCursor">
/// A handle to the cursor. The cursor must have been created by the
/// CreateCursor function or loaded by the LoadCursor or LoadImage
/// function. If this parameter is IntPtr.Zero, the cursor is removed
/// from the screen.
/// </param>
/// <returns>
/// The return value is the handle to the previous cursor, if there was one.
///
/// If there was no previous cursor, the return value is null.
/// </returns>
/// <remarks>
/// The cursor is set only if the new cursor is different from the
/// previous cursor; otherwise, the function returns immediately.
///
/// The cursor is a shared resource. A window should set the cursor
/// shape only when the cursor is in its client area or when the window
/// is capturing mouse input. In systems without a mouse, the window
/// should restore the previous cursor before the cursor leaves the
/// client area or before it relinquishes control to another window.
///
/// If your application must set the cursor while it is in a window,
/// make sure the class cursor for the specified window's class is set
/// to NULL. If the class cursor is not NULL, the system restores the
/// class cursor each time the mouse is moved.
///
/// The cursor is not shown on the screen if the internal cursor
/// display count is less than zero. This occurs if the application
/// uses the ShowCursor function to hide the cursor more times than to
/// show the cursor.
/// </remarks>
[DllImport("user32.dll")]
public static extern HCURSOR SetCursor(HCURSOR hCursor);
/// <summary>
/// Retrieves a handle to the current cursor.
/// </summary>
/// <returns>
/// The return value is the handle to the current cursor. If there is
/// no cursor, the return value is null.
/// </returns>
[DllImport("user32.dll")]
public static extern HCURSOR GetCursor();
#region Async input #region Async input
#region GetCursorPos #region GetCursorPos
@ -3007,6 +3146,57 @@ namespace OpenTK.Platform.Windows
#endregion #endregion
#region IconInfo
/// \internal
/// <summary>
/// Contains information about an icon or a cursor.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
struct IconInfo
{
/// <summary>
/// Specifies whether this structure defines an icon or a cursor. A
/// value of TRUE specifies an icon; FALSE specifies a cursor
/// </summary>
public bool fIcon;
/// <summary>
/// The x-coordinate of a cursor's hot spot. If this structure defines
/// an icon, the hot spot is always in the center of the icon, and
/// this member is ignored.
/// </summary>
public Int32 xHotspot;
/// <summary>
/// The y-coordinate of a cursor's hot spot. If this structure defines
/// an icon, the hot spot is always in the center of the icon, and
/// this member is ignored.
/// </summary>
public Int32 yHotspot;
/// <summary>
/// The icon bitmask bitmap. If this structure defines a black and
/// white icon, this bitmask is formatted so that the upper half is
/// the icon AND bitmask and the lower half is the icon XOR bitmask.
/// Under this condition, the height should be an even multiple of
/// two. If this structure defines a color icon, this mask only
/// defines the AND bitmask of the icon.
/// </summary>
public IntPtr hbmMask;
/// <summary>
/// A handle to the icon color bitmap. This member can be optional if
/// this structure defines a black and white icon. The AND bitmask of
/// hbmMask is applied with the SRCAND flag to the destination;
/// subsequently, the color bitmap is applied (using XOR) to the
/// destination by using the SRCINVERT flag.
/// </summary>
public IntPtr hbmColor;
}
#endregion
#endregion #endregion
#region --- Enums --- #region --- Enums ---

View file

@ -101,6 +101,8 @@ namespace OpenTK.Platform.Windows
KeyboardKeyEventArgs key_up = new KeyboardKeyEventArgs(); KeyboardKeyEventArgs key_up = new KeyboardKeyEventArgs();
KeyPressEventArgs key_press = new KeyPressEventArgs((char)0); KeyPressEventArgs key_press = new KeyPressEventArgs((char)0);
MouseCursor cursor = MouseCursor.Default;
IntPtr curson_handle = IntPtr.Zero;
int cursor_visible_count = 0; int cursor_visible_count = 0;
static readonly object SyncRoot = new object(); static readonly object SyncRoot = new object();
@ -386,6 +388,17 @@ namespace OpenTK.Platform.Windows
} }
} }
private IntPtr? HandleSetCursor(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
if (cursor != MouseCursor.Default)
{
Functions.SetCursor(curson_handle);
return new IntPtr(1);
}
return null;
}
void HandleChar(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) void HandleChar(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{ {
char c; char c;
@ -653,6 +666,8 @@ namespace OpenTK.Platform.Windows
IntPtr WindowProcedure(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) IntPtr WindowProcedure(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{ {
IntPtr? result = null;
switch (message) switch (message)
{ {
#region Size / Move / Style events #region Size / Move / Style events
@ -686,6 +701,10 @@ namespace OpenTK.Platform.Windows
HandleSize(handle, message, wParam, lParam); HandleSize(handle, message, wParam, lParam);
break; break;
case WindowMessage.SETCURSOR:
result = HandleSetCursor(handle, message, wParam, lParam);
break;
#endregion #endregion
#region Input events #region Input events
@ -772,8 +791,15 @@ namespace OpenTK.Platform.Windows
#endregion #endregion
} }
if (result.HasValue)
{
return result.Value;
}
else
{
return Functions.DefWindowProc(handle, message, wParam, lParam); return Functions.DefWindowProc(handle, message, wParam, lParam);
} }
}
private void EnableMouseTracking() private void EnableMouseTracking()
{ {
@ -1165,6 +1191,66 @@ namespace OpenTK.Platform.Windows
#endregion #endregion
#region Cursor
public MouseCursor Cursor
{
get
{
return cursor;
}
set
{
if (value != cursor)
{
var stride = value.Width *
(Bitmap.GetPixelFormatSize(System.Drawing.Imaging.PixelFormat.Format32bppArgb) / 8);
Bitmap bmp;
unsafe
{
fixed (byte* pixels = value.Rgba)
{
bmp = new Bitmap(value.Width, value.Height, stride,
System.Drawing.Imaging.PixelFormat.Format32bppArgb,
new IntPtr(pixels));
}
}
var iconInfo = new IconInfo();
var bmpIcon = bmp.GetHicon();
var success = Functions.GetIconInfo(bmpIcon, out iconInfo);
if (success)
{
iconInfo.xHotspot = value.X;
iconInfo.yHotspot = value.Y;
iconInfo.fIcon = false;
var icon = Functions.CreateIconIndirect(ref iconInfo);
if (icon != IntPtr.Zero)
{
// Currently using a custom cursor so destroy it
// once replaced
bool destoryOld = cursor != MouseCursor.Default;
cursor = value;
curson_handle = icon;
var oldCursor = Functions.SetCursor(icon);
if (destoryOld)
{
Functions.DestroyIcon(oldCursor);
}
}
}
}
}
}
#endregion
#region CursorVisible #region CursorVisible
public bool CursorVisible public bool CursorVisible

View file

@ -38,6 +38,11 @@ namespace OpenTK.Platform.X11
using Display = System.IntPtr; using Display = System.IntPtr;
using XPointer = System.IntPtr; using XPointer = System.IntPtr;
using XcursorBool = System.Int32;
using XcursorUInt = System.UInt32;
using XcursorDim = System.UInt32;
using XcursorPixel = System.UInt32;
// Randr and Xrandr // Randr and Xrandr
using Bool = System.Boolean; using Bool = System.Boolean;
using XRRScreenConfiguration = System.IntPtr; // opaque datatype using XRRScreenConfiguration = System.IntPtr; // opaque datatype
@ -579,6 +584,47 @@ XF86VidModeGetGammaRampSize(
#region X11 Structures #region X11 Structures
#region Xcursor
[StructLayout(LayoutKind.Sequential)]
unsafe struct XcursorImage
{
public XcursorUInt version;
public XcursorDim size;
public XcursorDim width;
public XcursorDim height;
public XcursorDim xhot;
public XcursorDim yhot;
public XcursorUInt delay;
public XcursorPixel* pixels;
}
[StructLayout(LayoutKind.Sequential)]
unsafe struct XcursorImages
{
public int nimage;
public XcursorImage **images;
public char *name;
}
[StructLayout(LayoutKind.Sequential)]
unsafe struct XcursorCursors
{
public Display dpy;
public int refcount;
public int ncursor;
public Cursor *cursors;
}
[StructLayout(LayoutKind.Sequential)]
unsafe struct XcursorAnimate
{
public XcursorCursors *cursors;
public int sequence;
}
#endregion
#region internal class XVisualInfo #region internal class XVisualInfo
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
@ -1388,6 +1434,7 @@ XF86VidModeGetGammaRampSize(
internal static partial class Functions internal static partial class Functions
{ {
internal const string X11Library = "libX11"; internal const string X11Library = "libX11";
internal const string XcursorLibrary = "libXcursor.so.1";
#region XCreateWindow #region XCreateWindow
@ -1434,6 +1481,19 @@ XF86VidModeGetGammaRampSize(
#endregion #endregion
#region Xcursor
[DllImport(XcursorLibrary)]
internal static unsafe extern XcursorImage* XcursorImageCreate(int width, int height);
[DllImport(XcursorLibrary)]
internal static unsafe extern void XcursorImageDestroy(XcursorImage* image);
[DllImport(XcursorLibrary)]
internal static unsafe extern Cursor XcursorImageLoadCursor(Display dpy, XcursorImage* image);
#endregion
#region XQueryKeymap #region XQueryKeymap
/* /*

View file

@ -115,6 +115,9 @@ namespace OpenTK.Platform.X11
bool isExiting; bool isExiting;
bool _decorations_hidden = false; bool _decorations_hidden = false;
MouseCursor cursor = MouseCursor.Default;
IntPtr cursorHandle;
bool cursor_visible = true; bool cursor_visible = true;
int mouse_rel_x, mouse_rel_y; int mouse_rel_x, mouse_rel_y;
@ -1460,6 +1463,48 @@ namespace OpenTK.Platform.X11
#endregion #endregion
#region Cursor
public MouseCursor Cursor
{
get
{
return cursor;
}
set
{
unsafe
{
using (new XLock(window.Display))
{
if (value == MouseCursor.Default)
{
Functions.XUndefineCursor(window.Display, window.Handle);
}
else
{
fixed(byte* pixels = value.Rgba)
{
var xcursorimage = Functions.XcursorImageCreate(value.Width, value.Height);
xcursorimage->xhot = (uint)value.X;
xcursorimage->yhot = (uint)value.Y;
xcursorimage->pixels = (uint*)pixels;
xcursorimage->delay = 0;
cursorHandle = Functions.XcursorImageLoadCursor(window.Display, xcursorimage);
Functions.XDefineCursor(window.Display, window.Handle, cursorHandle);
Functions.XcursorImageDestroy(xcursorimage);
}
}
cursor = value;
}
}
}
}
#endregion
#region CursorVisible
public bool CursorVisible public bool CursorVisible
{ {
get { return cursor_visible; } get { return cursor_visible; }
@ -1469,7 +1514,7 @@ namespace OpenTK.Platform.X11
{ {
using (new XLock(window.Display)) using (new XLock(window.Display))
{ {
Functions.XUndefineCursor(window.Display, window.Handle); Functions.XDefineCursor(window.Display, window.Handle, cursorHandle);
cursor_visible = true; cursor_visible = true;
} }
} }
@ -1486,6 +1531,8 @@ namespace OpenTK.Platform.X11
#endregion #endregion
#endregion
#region --- INativeGLWindow Members --- #region --- INativeGLWindow Members ---
#region public IInputDriver InputDriver #region public IInputDriver InputDriver
@ -1704,6 +1751,10 @@ namespace OpenTK.Platform.X11
{ {
using (new XLock(window.Display)) using (new XLock(window.Display))
{ {
if(cursorHandle != IntPtr.Zero)
{
Functions.XFreeCursor(window.Display, cursorHandle);
}
Functions.XFreeCursor(window.Display, EmptyCursor); Functions.XFreeCursor(window.Display, EmptyCursor);
Functions.XDestroyWindow(window.Display, window.Handle); Functions.XDestroyWindow(window.Display, window.Handle);
} }

View file

@ -0,0 +1,45 @@
#region License
//
// WindowIcon.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-2014 Stefanos Apostolopoulos
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#endregion
using System;
namespace OpenTK
{
/// <summary>
/// Stores a window icon. A window icon is defined
/// as a 2-dimensional buffer of RGBA values.
/// </summary>
public class WindowIcon
{
internal protected WindowIcon()
{
}
}
}