Merge branch 'thefiddler-cursor' into develop

This commit is contained in:
thefiddler 2014-04-28 09:38:54 +02:00
commit a07a61a003
18 changed files with 930 additions and 13 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

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

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.")]
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>
/// Gets or sets a value, indicating whether the mouse cursor is visible.
/// </summary>

View file

@ -211,12 +211,14 @@ namespace OpenTK.Input
{
args.Key = key;
args.ScanCode = scancode;
args.Modifiers = GetModifiers();
KeyDown(this, args);
}
else if (!state && KeyUp != null)
{
args.Key = key;
args.ScanCode = scancode;
args.Modifiers = GetModifiers();
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
#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
/// <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">
<PropertyGroup>
<ProjectType>Local</ProjectType>
@ -759,6 +759,7 @@
<Compile Include="Input\JoystickHat.cs" />
<Compile Include="Input\HatPosition.cs" />
<Compile Include="Input\JoystickHatState.cs" />
<Compile Include="MouseCursor.cs" />
<Compile Include="Input\KeyModifiers.cs" />
<Compile Include="Platform\MacOS\CocoaContext.cs" />
<Compile Include="Platform\MacOS\CocoaNativeWindow.cs" />
@ -794,6 +795,7 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="Platform\MacOS\Carbon\Cgl.cs" />
<Compile Include="WindowIcon.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
@ -823,5 +825,4 @@
</Properties>
</MonoDevelop>
</ProjectExtensions>
<ItemGroup />
</Project>

View file

@ -83,6 +83,8 @@ namespace OpenTK.Platform.MacOS
float mouse_rel_x;
float mouse_rel_y;
MouseCursor cursor = MouseCursor.Default;
#endregion
#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
{
get { return CG.CursorIsVisible(); }

View file

@ -112,6 +112,7 @@ namespace OpenTK.Platform.MacOS
//static readonly IntPtr selIsInFullScreenMode = Selector.Get("isInFullScreenMode");
//static readonly IntPtr selExitFullScreenModeWithOptions = Selector.Get("exitFullScreenModeWithOptions:");
//static readonly IntPtr selEnterFullScreenModeWithOptions = Selector.Get("enterFullScreenMode:withOptions:");
static readonly IntPtr selArrowCursor = Selector.Get("arrowCursor");
static readonly IntPtr NSDefaultRunLoopMode;
static readonly IntPtr NSCursor;
@ -142,8 +143,10 @@ namespace OpenTK.Platform.MacOS
private int normalLevel;
private bool shouldClose;
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;
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);
if (trackingAreaOwner == windowInfo.ViewHandle)
{
if (!cursorVisible)
if (selectedCursor != MouseCursor.Default)
{
SetCursorVisible(false);
SetCursor(selectedCursor);
}
cursorInsideWindow = true;
MouseEnter(this, EventArgs.Empty);
}
}
@ -428,11 +432,12 @@ namespace OpenTK.Platform.MacOS
var trackingAreaOwner = Cocoa.SendIntPtr(eventTrackingArea, selOwner);
if (trackingAreaOwner == windowInfo.ViewHandle)
{
if (!cursorVisible)
if (selectedCursor != MouseCursor.Default)
{
SetCursorVisible(true);
SetCursor(MouseCursor.Default);
}
cursorInsideWindow = false;
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
{
get { return cursorVisible; }
@ -966,11 +992,26 @@ namespace OpenTK.Platform.MacOS
private void SetCursorVisible(bool visible)
{
// Problem: Unlike the PC version, you can move the mouse out of the window.
// Perhaps use CG.WarpMouseCursorPosition to clamp mouse?
Carbon.CG.AssociateMouseAndMouseCursorPosition(visible);
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)
{
var options = (NSApplicationPresentationOptions)Cocoa.SendInt(NSApplication.Handle, selPresentationOptions);

View file

@ -33,6 +33,8 @@ using System.Runtime.InteropServices;
namespace OpenTK.Platform.SDL2
{
using Surface = IntPtr;
using Cursor = IntPtr;
partial class SDL
{
@ -77,6 +79,26 @@ namespace OpenTK.Platform.SDL2
// 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]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_AddEventWatch", ExactSpelling = true)]
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)]
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]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetNumDisplayModes", ExactSpelling = true)]
public static extern int GetNumDisplayModes(int displayIndex);

View file

@ -58,6 +58,8 @@ namespace OpenTK.Platform.SDL2
WindowState previous_window_state = WindowState.Normal;
WindowBorder window_border = WindowBorder.Resizable;
Icon icon;
MouseCursor cursor = MouseCursor.Default;
IntPtr sdl_cursor = IntPtr.Zero;
string window_title;
// 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> 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()
{
lock (sync)

View file

@ -856,6 +856,98 @@ namespace OpenTK.Platform.Windows
#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)]
public static extern BOOL SetForegroundWindow(HWND hWnd);
@ -1043,6 +1135,53 @@ namespace OpenTK.Platform.Windows
uint cbSize, MouseMovePoint* pointsIn,
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 GetCursorPos
@ -3007,6 +3146,57 @@ namespace OpenTK.Platform.Windows
#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
#region --- Enums ---

View file

@ -101,6 +101,8 @@ namespace OpenTK.Platform.Windows
KeyboardKeyEventArgs key_up = new KeyboardKeyEventArgs();
KeyPressEventArgs key_press = new KeyPressEventArgs((char)0);
MouseCursor cursor = MouseCursor.Default;
IntPtr curson_handle = IntPtr.Zero;
int cursor_visible_count = 0;
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)
{
char c;
@ -653,6 +666,8 @@ namespace OpenTK.Platform.Windows
IntPtr WindowProcedure(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
IntPtr? result = null;
switch (message)
{
#region Size / Move / Style events
@ -686,6 +701,10 @@ namespace OpenTK.Platform.Windows
HandleSize(handle, message, wParam, lParam);
break;
case WindowMessage.SETCURSOR:
result = HandleSetCursor(handle, message, wParam, lParam);
break;
#endregion
#region Input events
@ -772,7 +791,14 @@ namespace OpenTK.Platform.Windows
#endregion
}
return Functions.DefWindowProc(handle, message, wParam, lParam);
if (result.HasValue)
{
return result.Value;
}
else
{
return Functions.DefWindowProc(handle, message, wParam, lParam);
}
}
private void EnableMouseTracking()
@ -1164,7 +1190,78 @@ namespace OpenTK.Platform.Windows
public bool Exists { get { return exists; } }
#endregion
#region Cursor
public MouseCursor Cursor
{
get
{
return cursor;
}
set
{
if (value != cursor)
{
bool destoryOld = cursor != MouseCursor.Default;
IntPtr oldCursor = IntPtr.Zero;
if (value == MouseCursor.Default)
{
oldCursor = Functions.SetCursor(Functions.LoadCursor(CursorName.Arrow));
cursor = value;
}
else
{
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));
}
}
using (bmp)
{
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
cursor = value;
curson_handle = icon;
oldCursor = Functions.SetCursor(icon);
}
}
}
}
if (destoryOld && oldCursor != IntPtr.Zero)
{
Functions.DestroyIcon(oldCursor);
}
}
}
}
#endregion
#region CursorVisible
public bool CursorVisible

View file

@ -38,6 +38,11 @@ namespace OpenTK.Platform.X11
using Display = 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
using Bool = System.Boolean;
using XRRScreenConfiguration = System.IntPtr; // opaque datatype
@ -579,6 +584,47 @@ XF86VidModeGetGammaRampSize(
#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
[StructLayout(LayoutKind.Sequential)]
@ -1388,6 +1434,7 @@ XF86VidModeGetGammaRampSize(
internal static partial class Functions
{
internal const string X11Library = "libX11";
internal const string XcursorLibrary = "libXcursor.so.1";
#region XCreateWindow
@ -1434,6 +1481,19 @@ XF86VidModeGetGammaRampSize(
#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
/*

View file

@ -115,6 +115,9 @@ namespace OpenTK.Platform.X11
bool isExiting;
bool _decorations_hidden = false;
MouseCursor cursor = MouseCursor.Default;
IntPtr cursorHandle;
bool cursor_visible = true;
int mouse_rel_x, mouse_rel_y;
@ -1460,6 +1463,48 @@ namespace OpenTK.Platform.X11
#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
{
get { return cursor_visible; }
@ -1469,7 +1514,7 @@ namespace OpenTK.Platform.X11
{
using (new XLock(window.Display))
{
Functions.XUndefineCursor(window.Display, window.Handle);
Functions.XDefineCursor(window.Display, window.Handle, cursorHandle);
cursor_visible = true;
}
}
@ -1486,6 +1531,8 @@ namespace OpenTK.Platform.X11
#endregion
#endregion
#region --- INativeGLWindow Members ---
#region public IInputDriver InputDriver
@ -1704,6 +1751,10 @@ namespace OpenTK.Platform.X11
{
using (new XLock(window.Display))
{
if(cursorHandle != IntPtr.Zero)
{
Functions.XFreeCursor(window.Display, cursorHandle);
}
Functions.XFreeCursor(window.Display, EmptyCursor);
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()
{
}
}
}