Merge branch 'develop' into utf8

This commit is contained in:
Stefanos A 2014-01-16 11:39:22 +01:00
commit 9268b5ed7f
79 changed files with 7464 additions and 1450 deletions

1
.gitignore vendored
View file

@ -70,6 +70,7 @@ Binaries/
*.vspscc *.vspscc
*.vssscc *.vssscc
.builds .builds
*.pidb
# Visual C++ cache files # Visual C++ cache files
ipch/ ipch/

View file

@ -565,6 +565,7 @@
<None Include="..\..\Dependencies\x64\libSDL2.dylib"> <None Include="..\..\Dependencies\x64\libSDL2.dylib">
<Link>Dependencies\x64\libSDL2.dylib</Link> <Link>Dependencies\x64\libSDL2.dylib</Link>
</None> </None>
<Compile Include="OpenTK\Test\ExternalContext.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5"> <BootstrapperPackage Include="Microsoft.Net.Client.3.5">

View file

@ -0,0 +1,81 @@
// 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.Runtime.InteropServices;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
namespace Examples.Tests
{
[Example("External Context Test", ExampleCategory.OpenTK, "OpenGL")]
class ExternalContext
{
public static void Main()
{
using (Toolkit.Init(new ToolkitOptions { Backend = PlatformBackend.PreferNative }))
{
var window = Sdl2.CreateWindow("Test", 0, 0, 640, 480, WindowFlags.AllowHighDpi | WindowFlags.OpenGL);
var context = Sdl2.CreateContext(window);
Sdl2.MakeCurrent(window, context);
using (var dummy = new GraphicsContext(new ContextHandle(context), OpenTK.Platform.Utilities.CreateDummyWindowInfo()))
{
for (int i = 0; i < 100; i++)
{
Sdl2.PumpEvents();
GL.ClearColor(i / 100.0f, i / 100.0f, i / 100.0f, i / 100.0f);
GL.Clear(ClearBufferMask.ColorBufferBit);
Sdl2.SwapWindow(window);
}
Sdl2.DestroyWindow(window);
}
}
}
}
#region SDL2 bindings
public enum WindowFlags
{
Default = 0,
OpenGL = 0x00000002,
AllowHighDpi = 0x00002000,
}
static class Sdl2
{
const string lib = "SDL2.dll";
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_CreateWindow", ExactSpelling = true)]
public static extern IntPtr CreateWindow(string title, int x, int y, int w, int h, WindowFlags flags);
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_CreateContext", ExactSpelling = true)]
public static extern IntPtr CreateContext(IntPtr window);
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_DestroyWindow", ExactSpelling = true)]
public static extern void DestroyWindow(IntPtr window);
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetCurrentContext", ExactSpelling = true)]
public static extern IntPtr GetCurrentContext();
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetProcAddress", ExactSpelling = true)]
public static extern IntPtr GetAddress(string name);
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_MakeCurrent", ExactSpelling = true)]
public static extern int MakeCurrent(IntPtr window, IntPtr context);
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_PumpEvents", ExactSpelling = true)]
public static extern void PumpEvents();
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_SwapWindow", ExactSpelling = true)]
public static extern void SwapWindow(IntPtr window);
}
#endregion
}

View file

@ -25,9 +25,22 @@ namespace Examples.Tests
int texture; int texture;
bool mouse_in_window = false; bool mouse_in_window = false;
bool viewport_changed = true; bool viewport_changed = true;
bool refresh_text = true;
MouseState mouse, mouse_old; // time drift
KeyboardState keyboard, keyboard_old; Stopwatch watch = new Stopwatch();
double update_time, render_time;
// timing information
double timestamp;
int update_count;
int update_fps;
int render_count;
int render_fps;
// position of moving objects on screen
double variable_update_timestep_pos = -1;
double variable_refresh_timestep_pos = -1;
double fixed_update_timestep_pos = -1;
public GameWindowStates() public GameWindowStates()
: base(800, 600, GraphicsMode.Default) : base(800, 600, GraphicsMode.Default)
@ -40,20 +53,9 @@ namespace Examples.Tests
MouseEnter += delegate { mouse_in_window = true; }; MouseEnter += delegate { mouse_in_window = true; };
MouseLeave += delegate { mouse_in_window = false; }; MouseLeave += delegate { mouse_in_window = false; };
Move += delegate { refresh_text = true; };
Resize += delegate { refresh_text = true; };
WindowBorderChanged += delegate { refresh_text = true; };
WindowStateChanged += delegate { refresh_text = true; };
FocusedChanged += delegate { refresh_text = true; };
Mouse.Move += MouseMoveHandler; Mouse.Move += MouseMoveHandler;
Mouse.ButtonDown += MouseButtonHandler; Mouse.ButtonDown += MouseButtonHandler;
Mouse.ButtonUp += MouseButtonHandler; Mouse.ButtonUp += MouseButtonHandler;
foreach (var joystick in Joysticks)
{
joystick.Move += delegate { refresh_text = true; };
joystick.ButtonDown += delegate { refresh_text = true; };
joystick.ButtonUp += delegate { refresh_text = true; };
}
} }
private void KeyPressHandler(object sender, KeyPressEventArgs e) private void KeyPressHandler(object sender, KeyPressEventArgs e)
@ -94,18 +96,24 @@ namespace Examples.Tests
case Key.KeypadMinus: case Key.KeypadMinus:
case Key.Minus: Size -= new Size(16, 16); break; case Key.Minus: Size -= new Size(16, 16); break;
case Key.V:
VSync = VSync == VSyncMode.On ? VSyncMode.Off : VSyncMode.On;
break;
case Key.BracketLeft: TargetUpdateFrequency--; break;
case Key.BracketRight: TargetUpdateFrequency++; break;
case Key.Comma: TargetRenderFrequency--; break;
case Key.Period: TargetRenderFrequency++; break;
} }
} }
void MouseMoveHandler(object sender, MouseMoveEventArgs e) void MouseMoveHandler(object sender, MouseMoveEventArgs e)
{ {
refresh_text = true;
} }
void MouseButtonHandler(object sender, MouseButtonEventArgs e) void MouseButtonHandler(object sender, MouseButtonEventArgs e)
{ {
refresh_text = true;
if (e.Button == MouseButton.Left && e.IsPressed) if (e.Button == MouseButton.Left && e.IsPressed)
{ {
CursorVisible = false; CursorVisible = false;
@ -117,100 +125,114 @@ namespace Examples.Tests
return val > max ? max : val < min ? min : val; return val > max ? max : val < min ? min : val;
} }
static void DrawString(Graphics gfx, string str, int line) static float DrawString(Graphics gfx, string str, int line)
{ {
gfx.DrawString(str, TextFont, Brushes.White, new PointF(0, line * TextFont.Height)); return DrawString(gfx, str, line, 0);
} }
static void DrawString(Graphics gfx, string str, int line, float offset) static float DrawString(Graphics gfx, string str, int line, float offset)
{ {
gfx.DrawString(str, TextFont, Brushes.White, new PointF(offset, line * TextFont.Height)); gfx.DrawString(str, TextFont, Brushes.White, new PointF(offset, line * TextFont.Height));
return offset + gfx.MeasureString(str, TextFont).Width;
} }
static void DrawKeyboard(Graphics gfx, KeyboardState keyboard, int line) static int DrawKeyboards(Graphics gfx, int line)
{ {
const string str = "Keys pressed:"; line++;
float space = gfx.MeasureString(" ", TextFont).Width; DrawString(gfx, "Keyboard:", line++);
float offset = gfx.MeasureString(str, TextFont).Width + space; for (int i = 0; i < 4; i++)
DrawString(gfx, str, line);
for (int i = 0; i < (int)Key.LastKey; i++)
{ {
Key k = (Key)i; var state = OpenTK.Input.Keyboard.GetState(i);
if (keyboard[k]) if (state.IsConnected)
{ {
string key = k.ToString(); StringBuilder sb = new StringBuilder();
DrawString(gfx, key, line, offset); sb.Append(i);
offset += gfx.MeasureString(key, TextFont).Width + space; sb.Append(": ");
for (int key_index = 0; key_index < (int)Key.LastKey; key_index++)
{
Key k = (Key)key_index;
if (state[k])
{
sb.Append(k);
sb.Append(" ");
} }
} }
DrawString(gfx, sb.ToString(), line++);
}
}
return line;
} }
static void DrawMouse(Graphics gfx, MouseState mouse, int line) static int DrawMice(Graphics gfx, int line)
{ {
const string str = "Buttons pressed:"; line++;
float space = gfx.MeasureString(" ", TextFont).Width; DrawString(gfx, "Mouse:", line++);
float offset = gfx.MeasureString(str, TextFont).Width + space; for (int i = 0; i < 4; i++)
DrawString(gfx, str, line);
for (int i = 0; i < (int)MouseButton.LastButton; i++)
{ {
MouseButton b = (MouseButton)i; var state = OpenTK.Input.Mouse.GetState(i);
if (mouse[b]) if (state.IsConnected)
{ {
string button = b.ToString(); StringBuilder sb = new StringBuilder();
DrawString(gfx, button, line, offset); Vector3 pos = new Vector3(state.X, state.Y, state.WheelPrecise);
offset += gfx.MeasureString(button, TextFont).Width + space; sb.Append(i);
sb.Append(": ");
sb.Append(pos);
for (int button_index = 0; button_index < (int)MouseButton.LastButton; button_index++)
{
MouseButton b = (MouseButton)button_index;
if (state[b])
{
sb.Append(b);
sb.Append(" ");
} }
} }
DrawString(gfx, sb.ToString(), line++);
}
}
return line;
} }
static void DrawJoysticks(Graphics gfx, IList<JoystickDevice> joysticks, int line) static int DrawLegacyJoysticks(Graphics gfx, IList<JoystickDevice> joysticks, int line)
{ {
float space = gfx.MeasureString(" ", TextFont).Width; line++;
DrawString(gfx, "Legacy Joystick:", line++);
int joy_index = -1;
foreach (var joy in joysticks) foreach (var joy in joysticks)
{ {
string str = String.Format("Joystick '{0}': ", joy.Description); joy_index++;
DrawString(gfx, str, line); if (!String.IsNullOrEmpty(joy.Description))
{
StringBuilder sb = new StringBuilder();
sb.Append(joy_index);
sb.Append(": '");
sb.Append(joy.Description);
sb.Append("' ");
float offset = 0;
line++;
for (int i = 0; i < joy.Axis.Count; i++) for (int i = 0; i < joy.Axis.Count; i++)
{ {
string axis = joy.Axis[i].ToString(); sb.Append(joy.Axis[i]);
DrawString(gfx, axis, line, offset); sb.Append(" ");
offset += gfx.MeasureString(axis, TextFont).Width + space;
} }
offset = 0;
line++;
for (int i = 0; i < joy.Button.Count; i++) for (int i = 0; i < joy.Button.Count; i++)
{ {
string button = joy.Button[i].ToString(); sb.Append(joy.Button[i]);
DrawString(gfx, button, line, offset); sb.Append(" ");
offset += gfx.MeasureString(button, TextFont).Width + space; }
DrawString(gfx, sb.ToString(), line++);
}
} }
line++; return line;
}
} }
protected override void OnUpdateFrame(FrameEventArgs e) protected override void OnUpdateFrame(FrameEventArgs e)
{;
InputDriver.Poll();
mouse = OpenTK.Input.Mouse.GetState();
if (mouse != mouse_old)
refresh_text = true;
mouse_old = mouse;
keyboard = OpenTK.Input.Keyboard.GetState();
if (keyboard != keyboard_old)
refresh_text = true;
keyboard_old = keyboard;
if (refresh_text)
{ {
refresh_text = false; double clock_time = watch.Elapsed.TotalSeconds;
update_time += e.Time;
timestamp += e.Time;
update_count++;
using (Graphics gfx = Graphics.FromImage(TextBitmap)) using (Graphics gfx = Graphics.FromImage(TextBitmap))
{ {
@ -219,38 +241,105 @@ namespace Examples.Tests
gfx.Clear(Color.Black); gfx.Clear(Color.Black);
gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
// OpenGL information
DrawString(gfx, GL.GetString(StringName.Renderer), line++);
DrawString(gfx, GL.GetString(StringName.Version), line++);
DrawString(gfx, Context.GraphicsMode.ToString(), line++); DrawString(gfx, Context.GraphicsMode.ToString(), line++);
DrawString(gfx, String.Format("[1 - 4]: change WindowState (current: {0}).", this.WindowState), line++); // GameWindow information
DrawString(gfx, String.Format("[5 - 7]: change WindowBorder (current: {0}).", this.WindowBorder), line++); line++;
DrawString(gfx, String.Format("Focused: {0}.", this.Focused), line++); DrawString(gfx, "GameWindow:", line++);
DrawString(gfx, String.Format("Mouse {0} window.", mouse_in_window ? "inside" : "outside of"), line++); DrawString(gfx, String.Format("[1 - 4]:[5 - 7]: WindowState.{0}:WindowBorder.{1}",
DrawString(gfx, String.Format("Mouse visible: {0}", CursorVisible), line++); this.WindowState, this.WindowBorder), line++);
DrawString(gfx, String.Format("Mouse position (absolute): {0}", new Vector3(Mouse.X, Mouse.Y, Mouse.Wheel)), line++); DrawString(gfx, String.Format("[V]: VSync.{0}.", VSync), line++);
DrawString(gfx, String.Format("Mouse position (relative): {0}", new Vector3(mouse.X, mouse.Y, mouse.WheelPrecise)), line++); DrawString(gfx, String.Format("Bounds: {0}", Bounds), line++);
DrawString(gfx, String.Format("Window.Bounds: {0}", Bounds), line++); DrawString(gfx, String.Format("ClientRectangle: {0}", ClientRectangle), line++);
DrawString(gfx, String.Format("Window.Location: {0}, Size: {1}", Location, Size), line++); DrawString(gfx, String.Format("Mouse {0} and {1}. {2}.",
DrawString(gfx, String.Format("Window: {{X={0},Y={1},Width={2},Height={3}}}", X, Y, Width, Height), line++); mouse_in_window ? "inside" : "outside",
DrawString(gfx, String.Format("Window.ClientRectangle: {0}", ClientRectangle), line++); CursorVisible ? "visible" : "hidden",
DrawString(gfx, TypedText.ToString(), line++); Focused ? "Focused" : "Not focused"), line++);
DrawKeyboard(gfx, keyboard, line++); DrawString(gfx, String.Format("Mouse coordinates: {0}", new Vector3(Mouse.X, Mouse.Y, Mouse.WheelPrecise)), line++);
DrawMouse(gfx, mouse, line++);
DrawJoysticks(gfx, Joysticks, line++); // Timing information
line++;
DrawString(gfx, "Timing:", line++);
DrawString(gfx,
String.Format("Frequency: update {4} ({0:f2}/{1:f2}); render {5} ({2:f2}/{3:f2})",
UpdateFrequency, TargetUpdateFrequency,
RenderFrequency, TargetRenderFrequency,
update_fps, render_fps),
line++);
DrawString(gfx,
String.Format("Period: update {4:N4} ({0:f4}/{1:f4}); render {5:N4} ({2:f4}/{3:f4})",
UpdatePeriod, TargetUpdatePeriod,
RenderPeriod, TargetRenderPeriod,
1.0 / update_fps, 1.0 / render_fps),
line++);
DrawString(gfx, String.Format("Time: update {0:f4}; render {1:f4}",
UpdateTime, RenderTime), line++);
DrawString(gfx, String.Format("Drift: clock {0:f4}; update {1:f4}; render {2:f4}",
clock_time, clock_time - update_time, clock_time - render_time), line++);
DrawString(gfx, String.Format("Text: {0}", TypedText.ToString()), line++);
if (timestamp >= 1)
{
timestamp -= 1;
update_fps = update_count;
render_fps = render_count;
update_count = 0;
render_count = 0;
}
// Input information
line = DrawKeyboards(gfx, line);
line = DrawMice(gfx, line);
line = DrawJoysticks(gfx, line);
line = DrawLegacyJoysticks(gfx, Joysticks, line);
}
fixed_update_timestep_pos += TargetUpdatePeriod;
variable_update_timestep_pos += e.Time;
if (fixed_update_timestep_pos >= 1)
fixed_update_timestep_pos -= 2;
if (variable_update_timestep_pos >= 1)
variable_update_timestep_pos -= 2;
}
int DrawJoysticks(Graphics gfx, int line)
{
line++;
DrawString(gfx, "GamePad:", line++);
for (int i = 0; i < 4; i++)
{
GamePadCapabilities caps = GamePad.GetCapabilities(i);
GamePadState state = GamePad.GetState(i);
if (state.IsConnected)
{
DrawString(gfx, String.Format("{0}: {1}", i, caps), line++);
DrawString(gfx, state.ToString(), line++);
} }
} }
System.Drawing.Imaging.BitmapData data = TextBitmap.LockBits( line++;
new System.Drawing.Rectangle(0, 0, TextBitmap.Width, TextBitmap.Height), DrawString(gfx, "Joystick:", line++);
System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); for (int i = 0; i < 4; i++)
GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, TextBitmap.Width, TextBitmap.Height, PixelFormat.Bgra, {
PixelType.UnsignedByte, data.Scan0); JoystickCapabilities caps = Joystick.GetCapabilities(i);
TextBitmap.UnlockBits(data); JoystickState state = Joystick.GetState(i);
if (state.IsConnected)
{
DrawString(gfx, String.Format("{0}: {1}", i, caps), line++);
DrawString(gfx, state.ToString(), line++);
}
} }
return line;
}
protected override void OnLoad(EventArgs e) protected override void OnLoad(EventArgs e)
{ {
base.OnLoad(e); watch.Start();
GL.ClearColor(Color.MidnightBlue); GL.ClearColor(Color.MidnightBlue);
@ -274,31 +363,96 @@ namespace Examples.Tests
protected override void OnRenderFrame(FrameEventArgs e) protected override void OnRenderFrame(FrameEventArgs e)
{ {
base.OnRenderFrame(e); render_time += e.Time;
render_count++;
GL.Clear(ClearBufferMask.ColorBufferBit);
if (viewport_changed) if (viewport_changed)
{ {
viewport_changed = false; viewport_changed = false;
GL.Viewport(0, 0, Width, Height); GL.Viewport(0, 0, Width, Height);
Matrix4 ortho_projection = Matrix4.CreateOrthographicOffCenter(0, Width, Height, 0, -1, 1);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadMatrix(ref ortho_projection);
} }
GL.Clear(ClearBufferMask.ColorBufferBit); DrawText();
GL.Begin(BeginMode.Quads); DrawMovingObjects();
variable_refresh_timestep_pos += e.Time;
if (variable_refresh_timestep_pos >= 1)
variable_refresh_timestep_pos -= 2;
SwapBuffers();
}
// Uploads our text Bitmap to an OpenGL texture
// and displays is to screen.
private void DrawText()
{
System.Drawing.Imaging.BitmapData data = TextBitmap.LockBits(
new System.Drawing.Rectangle(0, 0, TextBitmap.Width, TextBitmap.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, TextBitmap.Width, TextBitmap.Height, PixelFormat.Bgra,
PixelType.UnsignedByte, data.Scan0);
TextBitmap.UnlockBits(data);
Matrix4 text_projection = Matrix4.CreateOrthographicOffCenter(0, Width, Height, 0, -1, 1);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadMatrix(ref text_projection);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();
GL.Color4(Color4.White);
GL.Enable(EnableCap.Texture2D);
GL.Begin(PrimitiveType.Quads);
GL.TexCoord2(0, 0); GL.Vertex2(0, 0); GL.TexCoord2(0, 0); GL.Vertex2(0, 0);
GL.TexCoord2(1, 0); GL.Vertex2(TextBitmap.Width, 0); GL.TexCoord2(1, 0); GL.Vertex2(TextBitmap.Width, 0);
GL.TexCoord2(1, 1); GL.Vertex2(TextBitmap.Width, TextBitmap.Height); GL.TexCoord2(1, 1); GL.Vertex2(TextBitmap.Width, TextBitmap.Height);
GL.TexCoord2(0, 1); GL.Vertex2(0, TextBitmap.Height); GL.TexCoord2(0, 1); GL.Vertex2(0, TextBitmap.Height);
GL.End(); GL.End();
GL.Disable(EnableCap.Texture2D);
}
SwapBuffers(); // Draws three moving objects, using three different timing methods:
// 1. fixed framerate based on TargetUpdatePeriod
// 2. variable framerate based on UpdateFrame e.Time
// 3. variable framerate based on RenderFrame e.Time
// If the timing implementation is correct, all three objects
// should be moving at the same speed, regardless of the current
// UpdatePeriod and RenderPeriod.
void DrawMovingObjects()
{
Matrix4 thing_projection = Matrix4.CreateOrthographic(2, 2, -1, 1);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadMatrix(ref thing_projection);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();
GL.Translate(fixed_update_timestep_pos, -0.2, 0);
GL.Color4(Color4.Red);
DrawRectangle();
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();
GL.Translate(variable_update_timestep_pos, -0.4, 0);
GL.Color4(Color4.DarkGoldenrod);
DrawRectangle();
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();
GL.Translate(variable_refresh_timestep_pos, -0.8, 0);
GL.Color4(Color4.DarkGreen);
DrawRectangle();
}
private void DrawRectangle()
{
GL.Begin(PrimitiveType.Quads);
GL.Vertex2(-0.05, -0.05);
GL.Vertex2(+0.05, -0.05);
GL.Vertex2(+0.05, +0.05);
GL.Vertex2(-0.05, +0.05);
GL.End();
} }
public static void Main() public static void Main()

View file

@ -76,8 +76,6 @@ namespace OpenTK
throw new ArgumentNullException("mode"); throw new ArgumentNullException("mode");
if (control == null) if (control == null)
throw new ArgumentNullException("control"); throw new ArgumentNullException("control");
if (!mode.Index.HasValue)
throw new GraphicsModeException("Invalid or unsupported GraphicsMode.");
this.mode = mode; this.mode = mode;

View file

@ -9,6 +9,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security; using System.Security;
@ -262,7 +263,13 @@ namespace OpenTK.Audio.OpenAL
/// <returns>A string containing the name of the Device.</returns> /// <returns>A string containing the name of the Device.</returns>
public static string GetString(IntPtr device, AlcGetString param) public static string GetString(IntPtr device, AlcGetString param)
{ {
return Marshal.PtrToStringAnsi(GetStringPrivate(device, param)); IntPtr pstr = GetStringPrivate(device, param);
string str = String.Empty;
if (pstr != IntPtr.Zero)
{
str = Marshal.PtrToStringAnsi(pstr);
}
return str;
} }
/// <summary>This function returns a List of strings related to the context.</summary> /// <summary>This function returns a List of strings related to the context.</summary>
@ -277,7 +284,16 @@ namespace OpenTK.Audio.OpenAL
public static IList<string> GetString(IntPtr device, AlcGetStringList param) public static IList<string> GetString(IntPtr device, AlcGetStringList param)
{ {
List<string> result = new List<string>(); List<string> result = new List<string>();
IntPtr t = GetStringPrivate(IntPtr.Zero, (AlcGetString)param);
// We cannot use Marshal.PtrToStringAnsi(),
// because alcGetString is defined to return either a nul-terminated string,
// or an array of nul-terminated strings terminated by an extra nul.
// Marshal.PtrToStringAnsi() will fail in the latter case (it will only
// return the very first string in the array.)
// We'll have to marshal this ourselves.
IntPtr t = GetStringPrivate(device, (AlcGetString)param);
if (t != IntPtr.Zero)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder(); System.Text.StringBuilder sb = new System.Text.StringBuilder();
byte b; byte b;
int offset = 0; int offset = 0;
@ -285,18 +301,37 @@ namespace OpenTK.Audio.OpenAL
{ {
b = Marshal.ReadByte(t, offset++); b = Marshal.ReadByte(t, offset++);
if (b != 0) if (b != 0)
sb.Append((char)b);
if (b == 0)
{ {
result.Add(sb.ToString()); sb.Append((char)b);
if (Marshal.ReadByte(t, offset) == 0) // offset already properly increased through ++
break; // 2x null
else
sb.Remove(0, sb.Length); // 1x null
} }
} while (true); else
{
// One string from the array is complete
result.Add(sb.ToString());
return (IList<string>)result; // Check whether the array has finished
// Note: offset already been increased through offset++ above
if (Marshal.ReadByte(t, offset) == 0)
{
// 2x consecutive nuls, we've read the whole array
break;
}
else
{
// Another string is starting, clear the StringBuilder
sb.Remove(0, sb.Length);
}
}
}
while (true);
}
else
{
Debug.Print("[Audio] Alc.GetString({0}, {1}) returned null.",
device, param);
}
return result;
} }
[DllImport(Alc.Lib, EntryPoint = "alcGetIntegerv", ExactSpelling = true, CallingConvention = Alc.Style, CharSet = CharSet.Ansi), SuppressUnmanagedCodeSecurity()] [DllImport(Alc.Lib, EntryPoint = "alcGetIntegerv", ExactSpelling = true, CallingConvention = Alc.Style, CharSet = CharSet.Ansi), SuppressUnmanagedCodeSecurity()]

View file

@ -206,8 +206,8 @@ namespace OpenTK
{ {
if (!OpenTK.Platform.SDL2.SDL.WasInit(0)) if (!OpenTK.Platform.SDL2.SDL.WasInit(0))
{ {
var flags = OpenTK.Platform.SDL2.SystemFlags.EVERYTHING; var flags =
flags &= ~OpenTK.Platform.SDL2.SystemFlags.AUDIO; OpenTK.Platform.SDL2.SystemFlags.VIDEO | Platform.SDL2.SystemFlags.TIMER;
if (OpenTK.Platform.SDL2.SDL.Init(flags) == 0) if (OpenTK.Platform.SDL2.SDL.Init(flags) == 0)
{ {
supported = true; supported = true;

View file

@ -1,4 +1,4 @@
#region License #region License
// //
// The Open Toolkit Library License // The Open Toolkit Library License
// //
@ -75,7 +75,9 @@ namespace OpenTK
{ {
#region --- Fields --- #region --- Fields ---
object exit_lock = new object(); const double MaxFrequency = 500.0; // Frequency cap for Update/RenderFrame events
readonly Stopwatch watch = new Stopwatch();
IGraphicsContext glContext; IGraphicsContext glContext;
@ -83,12 +85,19 @@ namespace OpenTK
double update_period, render_period; double update_period, render_period;
double target_update_period, target_render_period; double target_update_period, target_render_period;
// TODO: Implement these:
double update_time, render_time; double update_time; // length of last UpdateFrame event
double render_time; // length of last RenderFrame event
double update_timestamp; // timestamp of last UpdateFrame event
double render_timestamp; // timestamp of last RenderFrame event
double update_epsilon; // quantization error for UpdateFrame events
bool is_running_slowly; // true, when UpdatePeriod cannot reach TargetUpdatePeriod
VSyncMode vsync; VSyncMode vsync;
Stopwatch update_watch = new Stopwatch(), render_watch = new Stopwatch();
double next_render = 0.0, next_update = 0.0;
FrameEventArgs update_args = new FrameEventArgs(); FrameEventArgs update_args = new FrameEventArgs();
FrameEventArgs render_args = new FrameEventArgs(); FrameEventArgs render_args = new FrameEventArgs();
@ -404,8 +413,7 @@ namespace OpenTK
//Resize += DispatchUpdateAndRenderFrame; //Resize += DispatchUpdateAndRenderFrame;
Debug.Print("Entering main loop."); Debug.Print("Entering main loop.");
update_watch.Start(); watch.Start();
render_watch.Start();
while (true) while (true)
{ {
ProcessEvents(); ProcessEvents();
@ -429,101 +437,84 @@ namespace OpenTK
} }
} }
double ClampElapsed(double elapsed)
{
return MathHelper.Clamp(elapsed, 0.0, 1.0);
}
void DispatchUpdateAndRenderFrame(object sender, EventArgs e) void DispatchUpdateAndRenderFrame(object sender, EventArgs e)
{ {
RaiseUpdateFrame(update_watch, ref next_update, update_args); int is_running_slowly_retries = 4;
RaiseRenderFrame(render_watch, ref next_render, render_args); double timestamp = watch.Elapsed.TotalSeconds;
} double elapsed = 0;
void RaiseUpdateFrame(Stopwatch update_watch, ref double next_update, FrameEventArgs update_args) elapsed = ClampElapsed(timestamp - update_timestamp);
while (elapsed > 0 && elapsed + update_epsilon >= TargetUpdatePeriod)
{ {
int num_updates = 0; RaiseUpdateFrame(elapsed, ref timestamp);
double total_update_time = 0;
// Cap the maximum time drift to 1 second (e.g. when the process is suspended). // Calculate difference (positive or negative) between
double time = update_watch.Elapsed.TotalSeconds; // actual elapsed time and target elapsed time. We must
if (time <= 0) // compensate for this difference.
update_epsilon += elapsed - TargetUpdatePeriod;
// Prepare for next loop
elapsed = ClampElapsed(timestamp - update_timestamp);
if (TargetUpdatePeriod <= Double.Epsilon)
{ {
// Protect against negative Stopwatch.Elapsed values. // According to the TargetUpdatePeriod documentation,
// See http://connect.microsoft.com/VisualStudio/feedback/details/94083/stopwatch-returns-negative-elapsed-time // a TargetUpdatePeriod of zero means we will raise
update_watch.Reset(); // UpdateFrame events as fast as possible (one event
update_watch.Start(); // per ProcessEvents() call)
return;
}
if (time > 1.0)
time = 1.0;
// Raise UpdateFrame events until we catch up with our target update rate.
while (next_update - time <= 0 && time > 0)
{
next_update -= time;
update_args.Time = time;
OnUpdateFrameInternal(update_args);
time = update_time = Math.Max(update_watch.Elapsed.TotalSeconds, 0) - time;
// Stopwatches are not accurate over long time periods.
// We accumulate the total elapsed time into the time variable
// while reseting the Stopwatch frequently.
update_watch.Reset();
update_watch.Start();
// Don't schedule a new update more than 1 second in the future.
// Sometimes the hardware cannot keep up with updates
// (e.g. when the update rate is too high, or the UpdateFrame processing
// is too costly). This cap ensures we can catch up in a reasonable time
// once the load becomes lighter.
next_update += TargetUpdatePeriod;
next_update = Math.Max(next_update, -1.0);
total_update_time += update_time;
// Allow up to 10 consecutive UpdateFrame events to prevent the
// application from "hanging" when the hardware cannot keep up
// with the requested update rate.
if (++num_updates >= 10 || TargetUpdateFrequency == 0.0)
break; break;
} }
// Calculate statistics is_running_slowly = update_epsilon >= TargetUpdatePeriod;
if (num_updates > 0) if (is_running_slowly && --is_running_slowly_retries == 0)
{ {
update_period = total_update_time / (double)num_updates; // If UpdateFrame consistently takes longer than TargetUpdateFrame
// stop raising events to avoid hanging inside the UpdateFrame loop.
break;
} }
} }
void RaiseRenderFrame(Stopwatch render_watch, ref double next_render, FrameEventArgs render_args) elapsed = ClampElapsed(timestamp - render_timestamp);
if (elapsed > 0 && elapsed >= TargetRenderPeriod)
{ {
// Cap the maximum time drift to 1 second (e.g. when the process is suspended). RaiseRenderFrame(elapsed, ref timestamp);
double time = render_watch.Elapsed.TotalSeconds; }
if (time <= 0)
{
// Protect against negative Stopwatch.Elapsed values.
// See http://connect.microsoft.com/VisualStudio/feedback/details/94083/stopwatch-returns-negative-elapsed-time
render_watch.Reset();
render_watch.Start();
return;
} }
if (time > 1.0)
time = 1.0;
double time_left = next_render - time;
if (time_left <= 0.0 && time > 0) void RaiseUpdateFrame(double elapsed, ref double timestamp)
{ {
// Schedule next render event. The 1 second cap ensures // Raise UpdateFrame event
// the process does not appear to hang. update_args.Time = elapsed;
next_render = time_left + TargetRenderPeriod; OnUpdateFrameInternal(update_args);
if (next_render < -1.0)
next_render = -1.0;
render_watch.Reset(); // Update UpdatePeriod/UpdateFrequency properties
render_watch.Start(); update_period = elapsed;
if (time > 0) // Update UpdateTime property
update_timestamp = timestamp;
timestamp = watch.Elapsed.TotalSeconds;
update_time = timestamp - update_timestamp;
}
void RaiseRenderFrame(double elapsed, ref double timestamp)
{ {
render_period = render_args.Time = time; // Raise RenderFrame event
render_args.Time = elapsed;
OnRenderFrameInternal(render_args); OnRenderFrameInternal(render_args);
render_time = render_watch.Elapsed.TotalSeconds;
} // Update RenderPeriod/UpdateFrequency properties
} render_period = elapsed;
// Update RenderTime property
render_timestamp = timestamp;
timestamp = watch.Elapsed.TotalSeconds;
render_time = timestamp - render_timestamp;
} }
#endregion #endregion
@ -692,7 +683,7 @@ namespace OpenTK
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <para>A value of 0.0 indicates that RenderFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities).</para> /// <para>A value of 0.0 indicates that RenderFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities).</para>
/// <para>Values lower than 1.0Hz are clamped to 1.0Hz. Values higher than 200.0Hz are clamped to 200.0Hz.</para> /// <para>Values lower than 1.0Hz are clamped to 0.0. Values higher than 500.0Hz are clamped to 200.0Hz.</para>
/// </remarks> /// </remarks>
public double TargetRenderFrequency public double TargetRenderFrequency
{ {
@ -710,11 +701,11 @@ namespace OpenTK
{ {
TargetRenderPeriod = 0.0; TargetRenderPeriod = 0.0;
} }
else if (value <= 200.0) else if (value <= MaxFrequency)
{ {
TargetRenderPeriod = 1.0 / value; TargetRenderPeriod = 1.0 / value;
} }
else Debug.Print("Target render frequency clamped to 200.0Hz."); // TODO: Where is it actually performed? else Debug.Print("Target render frequency clamped to {0}Hz.", MaxFrequency);
} }
} }
@ -727,7 +718,7 @@ namespace OpenTK
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <para>A value of 0.0 indicates that RenderFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities).</para> /// <para>A value of 0.0 indicates that RenderFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities).</para>
/// <para>Values lower than 0.005 seconds (200Hz) are clamped to 0.0. Values higher than 1.0 seconds (1Hz) are clamped to 1.0.</para> /// <para>Values lower than 0.002 seconds (500Hz) are clamped to 0.0. Values higher than 1.0 seconds (1Hz) are clamped to 1.0.</para>
/// </remarks> /// </remarks>
public double TargetRenderPeriod public double TargetRenderPeriod
{ {
@ -739,7 +730,7 @@ namespace OpenTK
set set
{ {
EnsureUndisposed(); EnsureUndisposed();
if (value <= 0.005) if (value <= 1 / MaxFrequency)
{ {
target_render_period = 0.0; target_render_period = 0.0;
} }
@ -760,7 +751,7 @@ namespace OpenTK
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <para>A value of 0.0 indicates that UpdateFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities).</para> /// <para>A value of 0.0 indicates that UpdateFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities).</para>
/// <para>Values lower than 1.0Hz are clamped to 1.0Hz. Values higher than 200.0Hz are clamped to 200.0Hz.</para> /// <para>Values lower than 1.0Hz are clamped to 0.0. Values higher than 500.0Hz are clamped to 500.0Hz.</para>
/// </remarks> /// </remarks>
public double TargetUpdateFrequency public double TargetUpdateFrequency
{ {
@ -778,11 +769,11 @@ namespace OpenTK
{ {
TargetUpdatePeriod = 0.0; TargetUpdatePeriod = 0.0;
} }
else if (value <= 200.0) else if (value <= MaxFrequency)
{ {
TargetUpdatePeriod = 1.0 / value; TargetUpdatePeriod = 1.0 / value;
} }
else Debug.Print("Target update frequency clamped to 200.0Hz."); // TODO: Where is it actually performed? else Debug.Print("Target render frequency clamped to {0}Hz.", MaxFrequency);
} }
} }
@ -795,7 +786,7 @@ namespace OpenTK
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <para>A value of 0.0 indicates that UpdateFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities).</para> /// <para>A value of 0.0 indicates that UpdateFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities).</para>
/// <para>Values lower than 0.005 seconds (200Hz) are clamped to 0.0. Values higher than 1.0 seconds (1Hz) are clamped to 1.0.</para> /// <para>Values lower than 0.002 seconds (500Hz) are clamped to 0.0. Values higher than 1.0 seconds (1Hz) are clamped to 1.0.</para>
/// </remarks> /// </remarks>
public double TargetUpdatePeriod public double TargetUpdatePeriod
{ {
@ -807,7 +798,7 @@ namespace OpenTK
set set
{ {
EnsureUndisposed(); EnsureUndisposed();
if (value <= 0.005) if (value <= 1 / MaxFrequency)
{ {
target_update_period = 0.0; target_update_period = 0.0;
} }
@ -815,7 +806,7 @@ namespace OpenTK
{ {
target_update_period = value; target_update_period = value;
} }
else Debug.Print("Target update period clamped to 1.0 seconds."); // TODO: Where is it actually performed? else Debug.Print("Target update period clamped to 1.0 seconds.");
} }
} }

View file

@ -39,14 +39,13 @@ namespace OpenTK.Graphics
/// </summary> /// </summary>
public sealed class GraphicsContext : IGraphicsContext, IGraphicsContextInternal public sealed class GraphicsContext : IGraphicsContext, IGraphicsContextInternal
{ {
public delegate IntPtr GetAddressDelegate(string function);
public delegate ContextHandle GetCurrentContextDelegate();
#region --- Fields --- #region --- Fields ---
IGraphicsContext implementation; // The actual render context implementation for the underlying platform. IGraphicsContext implementation; // The actual render context implementation for the underlying platform.
bool disposed; bool disposed;
// Indicates that this context was created through external means, e.g. Tao.Sdl or GLWidget#.
// In this case, We'll assume that the external program will manage the lifetime of this
// context - we'll not destroy it manually.
readonly bool IsExternal;
bool check_errors = true; bool check_errors = true;
// Cache for the context handle. We need this for RemoveContext() // Cache for the context handle. We need this for RemoveContext()
// in case the user does not call Dispose(). When this happens, // in case the user does not call Dispose(). When this happens,
@ -67,17 +66,6 @@ namespace OpenTK.Graphics
#region --- Constructors --- #region --- Constructors ---
// Necessary to allow creation of dummy GraphicsContexts (see CreateDummyContext static method).
GraphicsContext(ContextHandle handle)
{
implementation = new OpenTK.Platform.Dummy.DummyGLContext(handle);
lock (SyncRoot)
{
AddContext(this);
}
}
/// <summary> /// <summary>
/// Constructs a new GraphicsContext with the specified GraphicsMode and attaches it to the specified window. /// Constructs a new GraphicsContext with the specified GraphicsMode and attaches it to the specified window.
/// </summary> /// </summary>
@ -163,12 +151,65 @@ namespace OpenTK.Graphics
} }
} }
/// <summary>
/// Initializes a new instance of the <see cref="OpenTK.Graphics.GraphicsContext"/> class using
/// an external context handle that was created by a third-party library.
/// </summary>
/// <param name="handle">
/// A valid, unique handle for an external OpenGL context, or <c>ContextHandle.Zero</c> to use the current context.
/// It is an error to specify a handle that has been created through OpenTK or that has been passed to OpenTK before.
/// </param>
/// <param name="getAddress">
/// A <c>GetAddressDelegate</c> instance that accepts the name of an OpenGL function and returns
/// a valid function pointer, or <c>IntPtr.Zero</c> if that function is not supported. This delegate should be
/// implemented using the same toolkit that created the OpenGL context (i.e. if the context was created with
/// SDL_GL_CreateContext(), then this delegate should use SDL_GL_GetProcAddress() to retrieve function
/// pointers.)
/// </param>
/// <param name="getCurrent">
/// A <c>GetCurrentContextDelegate</c> instance that returns the handle of the current OpenGL context,
/// or <c>IntPtr.Zero</c> if no context is current on the calling thread. This delegate should be implemented
/// using the same toolkit that created the OpenGL context (i.e. if the context was created with
/// SDL_GL_CreateContext(), then this delegate should use SDL_GL_GetCurrentContext() to retrieve
/// the current context.)
/// </param>
public GraphicsContext(ContextHandle handle, GetAddressDelegate getAddress, GetCurrentContextDelegate getCurrent)
{
if (getAddress == null || getCurrent == null)
throw new ArgumentNullException();
lock (SyncRoot)
{
// Replace a zero-handle by the current context, if any
if (handle == ContextHandle.Zero)
{
handle = getCurrent();
}
// Make sure this handle corresponds to a valid, unique OpenGL context
if (handle == ContextHandle.Zero)
{
throw new GraphicsContextMissingException();
}
else if (available_contexts.ContainsKey(handle))
{
throw new InvalidOperationException("Context handle has already been added");
}
// We have a valid handle for an external OpenGL context, wrap it into a
// DummyGLContext instance.
implementation = new Platform.Dummy.DummyGLContext(handle, getAddress);
GetCurrentContext = getCurrent ?? GetCurrentContext;
AddContext(this);
}
implementation.LoadAll();
}
/// <summary> /// <summary>
/// Constructs a new GraphicsContext from a pre-existing context created outside of OpenTK. /// Constructs a new GraphicsContext from a pre-existing context created outside of OpenTK.
/// </summary> /// </summary>
/// <param name="handle">The handle of the existing context. This must be a valid, unique handle that is not known to OpenTK.</param> /// <param name="handle">The handle of the existing context. This must be a valid, unique handle that is not known to OpenTK.</param>
/// <param name="window">The window this context is bound to. This must be a valid window obtained through Utilities.CreateWindowInfo.</param> /// <param name="window">This parameter is reserved.</param>
/// <exception cref="GraphicsContextException">Occurs if handle is identical to a context already registered with OpenTK.</exception>
public GraphicsContext(ContextHandle handle, IWindowInfo window) public GraphicsContext(ContextHandle handle, IWindowInfo window)
: this(handle, window, null, 1, 0, GraphicsContextFlags.Default) : this(handle, window, null, 1, 0, GraphicsContextFlags.Default)
{ } { }
@ -177,40 +218,14 @@ namespace OpenTK.Graphics
/// Constructs a new GraphicsContext from a pre-existing context created outside of OpenTK. /// Constructs a new GraphicsContext from a pre-existing context created outside of OpenTK.
/// </summary> /// </summary>
/// <param name="handle">The handle of the existing context. This must be a valid, unique handle that is not known to OpenTK.</param> /// <param name="handle">The handle of the existing context. This must be a valid, unique handle that is not known to OpenTK.</param>
/// <param name="window">The window this context is bound to. This must be a valid window obtained through Utilities.CreateWindowInfo.</param> /// <param name="window">This parameter is reserved.</param>
/// <param name="shareContext">A different context that shares resources with this instance, if any. /// <param name="shareContext">This parameter is reserved.</param>
/// Pass null if the context is not shared or if this is the first GraphicsContext instruct you construct.</param> /// <param name="major">This parameter is reserved.</param>
/// <param name="major">The major version of the context (e.g. "2" for "2.1").</param> /// <param name="minor">This parameter is reserved.</param>
/// <param name="minor">The minor version of the context (e.g. "1" for "2.1").</param> /// <param name="flags">This parameter is reserved..</param>
/// <param name="flags">A bitwise combination of <see cref="GraphicsContextFlags"/> that describe this context.</param>
/// <exception cref="GraphicsContextException">Occurs if handle is identical to a context already registered with OpenTK.</exception>
public GraphicsContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, int major, int minor, GraphicsContextFlags flags) public GraphicsContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, int major, int minor, GraphicsContextFlags flags)
: this(handle) : this(handle, Platform.Utilities.CreateGetAddress(), Factory.Default.CreateGetCurrentGraphicsContext())
{ { }
lock (SyncRoot)
{
IsExternal = true;
if (handle == ContextHandle.Zero)
{
implementation = new OpenTK.Platform.Dummy.DummyGLContext(handle);
}
else if (available_contexts.ContainsKey(handle))
{
throw new GraphicsContextException("Context already exists.");
}
else
{
switch ((flags & GraphicsContextFlags.Embedded) == GraphicsContextFlags.Embedded)
{
case false: implementation = Factory.Default.CreateGLContext(handle, window, shareContext, direct_rendering, major, minor, flags); break;
case true: implementation = Factory.Embedded.CreateGLContext(handle, window, shareContext, direct_rendering, major, minor, flags); break;
}
}
(this as IGraphicsContextInternal).LoadAll();
}
}
#endregion #endregion
@ -309,6 +324,7 @@ namespace OpenTK.Graphics
/// <para>Instances created by this method will not be functional. Instance methods will have no effect.</para> /// <para>Instances created by this method will not be functional. Instance methods will have no effect.</para>
/// <para>This method requires that a context is current on the calling thread.</para> /// <para>This method requires that a context is current on the calling thread.</para>
/// </remarks> /// </remarks>
[Obsolete("Use GraphicsContext(ContextHandle, IWindowInfo) constructor instead")]
public static GraphicsContext CreateDummyContext() public static GraphicsContext CreateDummyContext()
{ {
ContextHandle handle = GetCurrentContext(); ContextHandle handle = GetCurrentContext();
@ -326,12 +342,13 @@ namespace OpenTK.Graphics
/// <remarks> /// <remarks>
/// <para>Instances created by this method will not be functional. Instance methods will have no effect.</para> /// <para>Instances created by this method will not be functional. Instance methods will have no effect.</para>
/// </remarks> /// </remarks>
[Obsolete("Use GraphicsContext(ContextHandle, IWindowInfo) constructor instead")]
public static GraphicsContext CreateDummyContext(ContextHandle handle) public static GraphicsContext CreateDummyContext(ContextHandle handle)
{ {
if (handle == ContextHandle.Zero) if (handle == ContextHandle.Zero)
throw new ArgumentOutOfRangeException("handle"); throw new ArgumentOutOfRangeException("handle");
return new GraphicsContext(handle); return new GraphicsContext(handle, (IWindowInfo)null);
} }
#endregion #endregion
@ -352,7 +369,6 @@ namespace OpenTK.Graphics
#region public static IGraphicsContext CurrentContext #region public static IGraphicsContext CurrentContext
internal delegate ContextHandle GetCurrentContextDelegate();
internal static GetCurrentContextDelegate GetCurrentContext; internal static GetCurrentContextDelegate GetCurrentContext;
/// <summary> /// <summary>
@ -628,6 +644,11 @@ namespace OpenTK.Graphics
} }
} }
/// <summary>
/// Marks this context as deleted, but does not actually release unmanaged resources
/// due to the threading requirements of OpenGL. Use <see cref="GraphicsContext.Dispose"/>
/// instead.
/// </summary>
~GraphicsContext() ~GraphicsContext()
{ {
Dispose(false); Dispose(false);

View file

@ -0,0 +1,166 @@
//
// GamePadButton.cs
//
// Author:
// robert <${AuthorEmail}>
//
// Copyright (c) 2012 robert
//
// 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.
using System;
namespace OpenTK.Input
{
/// <summary>
/// Enumerates available buttons for a <c>GamePad</c> device.
/// </summary>
[Flags]
public enum Buttons
{
/// <summary>
/// DPad up direction button
/// </summary>
DPadUp = 1 << 0,
/// <summary>
/// DPad down direction button
/// </summary>
DPadDown = 1 << 1,
/// <summary>
/// DPad left direction button
/// </summary>
DPadLeft = 1 << 2,
/// <summary>
/// DPad right direction button
/// </summary>
DPadRight = 1 << 3,
/// <summary>
/// Start button
/// </summary>
Start = 1 << 4,
/// <summary>
/// Back button
/// </summary>
Back = 1 << 5,
/// <summary>
/// Left stick button
/// </summary>
LeftStick = 1 << 6,
/// <summary>
/// Right stick button
/// </summary>
RightStick = 1 << 7,
/// <summary>
/// Left shoulder button
/// </summary>
LeftShoulder = 1 << 8,
/// <summary>
/// Right shoulder button
/// </summary>
RightShoulder = 1 << 9,
/// <summary>
/// Home button
/// </summary>
Home = 1 << 11,
/// <summary>
/// Home button
/// </summary>
BigButton = Home,
/// <summary>
/// A button
/// </summary>
A = 1 << 12,
/// <summary>
/// B button
/// </summary>
B = 1 << 13,
/// <summary>
/// X button
/// </summary>
X = 1 << 14,
/// <summary>
/// Y button
/// </summary>
Y = 1 << 15,
/// <summary>
/// Left thumbstick left direction button
/// </summary>
LeftThumbstickLeft = 1 << 21,
/// <summary>
/// Right trigger button
/// </summary>
RightTrigger = 1 << 22,
/// <summary>
/// Left trigger button
/// </summary>
LeftTrigger = 1 << 23,
/// <summary>
/// Right thumbstick up direction button
/// </summary>
RightThumbstickUp = 1 << 24,
/// <summary>
/// Right thumbstick down direction button
/// </summary>
RightThumbstickDown = 1 << 25,
/// <summary>
/// Right stick right direction button
/// </summary>
RightThumbstickRight = 1 << 26,
/// <summary>
/// Right stick left direction button
/// </summary>
RightThumbstickLeft = 1 << 27,
/// <summary>
/// Left stick up direction button
/// </summary>
LeftThumbstickUp = 1 << 28,
/// <summary>
/// Left stick down direction button
/// </summary>
LeftThumbstickDown = 1 << 29,
/// <summary>
/// Left stick right direction button
/// </summary>
LeftThumbstickRight = 1 << 30,
}
}

View file

@ -0,0 +1,41 @@
#region License
//
// ConfigurationType.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.Input
{
enum ConfigurationType
{
Unmapped = 0,
Axis,
Button
}
}

View file

@ -1,27 +1,29 @@
#region License #region License
// //
// The Open Toolkit Library License // GamePad.cs
// //
// Copyright (c) 2006 - 2009 the Open Toolkit library. // 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 // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights to // in the Software without restriction, including without limitation the rights
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// the Software, and to permit persons to whom the Software is furnished to do // copies of the Software, and to permit persons to whom the Software is
// so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in
// copies or substantial portions of the Software. // all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // THE SOFTWARE.
// OTHER DEALINGS IN THE SOFTWARE.
// //
#endregion #endregion
@ -30,17 +32,65 @@ using System;
namespace OpenTK.Input namespace OpenTK.Input
{ {
/// <summary> /// <summary>
/// Provides access to GamePad devices. Note: this API is not implemented yet. /// Provides access to GamePad devices.
/// A GamePad device offers a well-defined layout with
/// one direction-pad, two thumbsticks, two triggers,
/// four main buttons (A, B, X, Y) and up to seven
/// auxilliary buttons.
/// Use <c>GetCapabilities</c> to retrieve the exact
/// capabilities of a given device.
/// Use <c>GetState</c> to retrieve the current state
/// of a given device.
/// </summary> /// </summary>
public class GamePad public sealed class GamePad
{ {
#region Constructors internal const int MaxAxisCount = 10;
internal const int MaxDPadCount = 2;
static GamePad() static readonly IGamePadDriver driver =
Platform.Factory.Default.CreateGamePadDriver();
private GamePad() { }
/// <summary>
/// Retrieves a <c>GamePadCapabilities</c> structure describing the
/// capabilities of a gamepad device.
/// </summary>
/// <param name="index">The zero-based index of a gamepad device.</param>
/// <returns>A <c>GamePadCapabilities</c> structure describing the capabilities of the gamepad device.</returns>
public static GamePadCapabilities GetCapabilities(int index)
{ {
throw new NotImplementedException(); if (index < 0)
throw new IndexOutOfRangeException();
return driver.GetCapabilities(index);
} }
#endregion /// <summary>
/// Retrieves the <c>GamePadState</c> for the specified gamepad device.
/// </summary>
/// <param name="index">The zero-based index of a gamepad device.</param>
/// <returns>A <c>GamePadState</c> structure describing the state of the gamepad device.</returns>
public static GamePadState GetState(int index)
{
return driver.GetState(index);
}
/// <summary>
/// Sets the vibration intensity for the left and right motors of this <see cref="GamePad"/>
/// </summary>
/// <returns>
/// <c>true</c>, if vibration was set, <c>false</c> otherwise. This method can return false
/// if the <c>GamePad</c> hardware does not support vibration or if it cannot respond to
/// the command for any reason. Do not loop until this becomes true, but rather ignore
/// a return value of false.
/// </returns>
/// <param name="index">A zero-based device index for the <c>GamePad</c> device to affect</param>
/// <param name="left">The vibration intensity for the left motor, between 0.0 and 1.0.</param>
/// <param name="right">The vibration intensity for the right motor, between 0.0 and 1.0.</param>
public static bool SetVibration(int index, float left, float right)
{
return driver.SetVibration(index, left, right);
}
} }
} }

View file

@ -25,32 +25,17 @@
// THE SOFTWARE. // THE SOFTWARE.
using System; using System;
namespace OpenTK namespace OpenTK.Input
{ {
public enum GamePadAxis [Flags]
internal enum GamePadAxes : byte
{ {
/// <summary>The first axis of the gamepad.</summary> LeftX = 1 << 0,
Axis0 = 0, LeftY = 1 << 1,
/// <summary>The second axis of the gamepad.</summary> LeftTrigger = 1 << 2,
Axis1, RightX = 1 << 3,
/// <summary>The third axis of the gamepad.</summary> RightY = 1 << 4,
Axis2, RightTrigger = 1 << 5,
/// <summary>The fourth axis of the gamepad.</summary>
Axis3,
/// <summary>The fifth axis of the gamepad.</summary>
Axis4,
/// <summary>The sixth axis of the gamepad.</summary>
Axis5,
/// <summary>The seventh axis of the gamepad.</summary>
Axis6,
/// <summary>The eighth axis of the gamepad.</summary>
Axis7,
/// <summary>The ninth axis of the gamepad.</summary>
Axis8,
/// <summary>The tenth axis of the gamepad.</summary>
Axis9,
/// <summary>The last axis of the gamepad.</summary>
LastAxis
} }
} }

View file

@ -1,68 +0,0 @@
//
// GamePadButton.cs
//
// Author:
// robert <${AuthorEmail}>
//
// Copyright (c) 2012 robert
//
// 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.
using System;
namespace OpenTK
{
public enum GamePadButton
{
/// <summary>The first button of the gamepad.</summary>
Button0 = 0,
/// <summary>The second button of the gamepad.</summary>
Button1,
/// <summary>The third button of the gamepad.</summary>
Button2,
/// <summary>The fourth button of the gamepad.</summary>
Button3,
/// <summary>The fifth button of the gamepad.</summary>
Button4,
/// <summary>The sixth button of the gamepad.</summary>
Button5,
/// <summary>The seventh button of the gamepad.</summary>
Button6,
/// <summary>The eighth button of the gamepad.</summary>
Button7,
/// <summary>The ninth button of the gamepad.</summary>
Button8,
/// <summary>The tenth button of the gamepad.</summary>
Button9,
/// <summary>The eleventh button of the gamepad.</summary>
Button10,
/// <summary>The twelfth button of the gamepad.</summary>
Button11,
/// <summary>The thirteenth button of the gamepad.</summary>
Button12,
/// <summary>The fourteenth button of the gamepad.</summary>
Button13,
/// <summary>The fifteenth button of the gamepad.</summary>
Button14,
/// <summary>The sixteenth button of the gamepad.</summary>
Button15,
/// <summary>The last button of the gamepad.</summary>
LastButton
}
}

View file

@ -0,0 +1,216 @@
#region License
//
// GamePadButtons.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.Input
{
/// <summary>
/// Describes the <see cref="ButtonState"/> of <see cref="GamePad"/> <see cref="Buttons"/>.
/// </summary>
public struct GamePadButtons : IEquatable<GamePadButtons>
{
Buttons buttons;
/// <summary>
/// Initializes a new instance of the <see cref="OpenTK.Input.GamePadButtons"/> structure.
/// </summary>
/// <param name="state">A bitmask containing the button state.</param>
public GamePadButtons(Buttons state)
{
buttons = state;
}
#region Public Members
/// <summary>
/// Gets the <see cref="ButtonState"/> for the A button.
/// </summary>
public ButtonState A
{
get { return GetButton(Buttons.A); }
}
/// <summary>
/// Gets the <see cref="ButtonState"/> for the B button.
/// </summary>
public ButtonState B
{
get { return GetButton(Buttons.B); }
}
/// <summary>
/// Gets the <see cref="ButtonState"/> for the X button.
/// </summary>
public ButtonState X
{
get { return GetButton(Buttons.X); }
}
/// <summary>
/// Gets the <see cref="ButtonState"/> for the Y button.
/// </summary>
public ButtonState Y
{
get { return GetButton(Buttons.Y); }
}
/// <summary>
/// Gets the <see cref="ButtonState"/> for the Back button.
/// </summary>
public ButtonState Back
{
get { return GetButton(Buttons.Back); }
}
/// <summary>
/// Gets the <see cref="ButtonState"/> for the big button.
/// This button is also known as Home or Guide.
/// </summary>
public ButtonState BigButton
{
get { return GetButton(Buttons.BigButton); }
}
/// <summary>
/// Gets the <see cref="ButtonState"/> for the left shoulder button.
/// </summary>
public ButtonState LeftShoulder
{
get { return GetButton(Buttons.LeftShoulder); }
}
/// <summary>
/// Gets the <see cref="ButtonState"/> for the left stick button.
/// This button represents a left stick that is pressed in.
/// </summary>
public ButtonState LeftStick
{
get { return GetButton(Buttons.LeftStick); }
}
/// <summary>
/// Gets the <see cref="ButtonState"/> for the right shoulder button.
/// </summary>
public ButtonState RightShoulder
{
get { return GetButton(Buttons.RightShoulder); }
}
/// <summary>
/// Gets the <see cref="ButtonState"/> for the right stick button.
/// This button represents a right stick that is pressed in.
/// </summary>
public ButtonState RightStick
{
get { return GetButton(Buttons.RightStick); }
}
/// <summary>
/// Gets the <see cref="ButtonState"/> for the starth button.
/// </summary>
public ButtonState Start
{
get { return GetButton(Buttons.Start); }
}
/// <param name="left">A <see cref="GamePadButtons"/> instance to test for equality.</param>
/// <param name="right">A <see cref="GamePadButtons"/> instance to test for equality.</param>
public static bool operator ==(GamePadButtons left, GamePadButtons right)
{
return left.Equals(right);
}
/// <param name="left">A <see cref="GamePadButtons"/> instance to test for inequality.</param>
/// <param name="right">A <see cref="GamePadButtons"/> instance to test for inequality.</param>
public static bool operator !=(GamePadButtons left, GamePadButtons right)
{
return !left.Equals(right);
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.GamePadButtons"/>.
/// </summary>
/// <returns>A <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.GamePadButtons"/>.</returns>
public override string ToString()
{
return Convert.ToString((int)buttons, 2).PadLeft(10, '0');
}
/// <summary>
/// Serves as a hash function for a <see cref="OpenTK.Input.GamePadButtons"/> object.
/// </summary>
/// <returns>A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
/// hash table.</returns>
public override int GetHashCode()
{
return buttons.GetHashCode();
}
/// <summary>
/// Determines whether the specified <see cref="System.Object"/> is equal to the current <see cref="OpenTK.Input.GamePadButtons"/>.
/// </summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with the current <see cref="OpenTK.Input.GamePadButtons"/>.</param>
/// <returns><c>true</c> if the specified <see cref="System.Object"/> is equal to the current
/// <see cref="OpenTK.Input.GamePadButtons"/>; otherwise, <c>false</c>.</returns>
public override bool Equals(object obj)
{
return
obj is GamePadButtons &&
Equals((GamePadButtons)obj);
}
#endregion
#region IEquatable<GamePadButtons> Members
/// <summary>
/// Determines whether the specified <see cref="OpenTK.Input.GamePadButtons"/> is equal to the current <see cref="OpenTK.Input.GamePadButtons"/>.
/// </summary>
/// <param name="other">The <see cref="OpenTK.Input.GamePadButtons"/> to compare with the current <see cref="OpenTK.Input.GamePadButtons"/>.</param>
/// <returns><c>true</c> if the specified <see cref="OpenTK.Input.GamePadButtons"/> is equal to the current
/// <see cref="OpenTK.Input.GamePadButtons"/>; otherwise, <c>false</c>.</returns>
public bool Equals(GamePadButtons other)
{
return buttons == other.buttons;
}
#endregion
#region Private Members
ButtonState GetButton(Buttons b)
{
return (buttons & b) != 0 ? ButtonState.Pressed : ButtonState.Released;
}
#endregion
}
}

View file

@ -0,0 +1,395 @@
#region License
//
// GamePadCapabilities.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-2013 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.Input
{
/// <summary>
/// Describes the capabilities of a <c>GamePad</c> input device.
/// </summary>
public struct GamePadCapabilities : IEquatable<GamePadCapabilities>
{
Buttons buttons;
GamePadAxes axes;
byte gamepad_type;
bool is_connected;
#region Constructors
internal GamePadCapabilities(GamePadType type, GamePadAxes axes, Buttons buttons, bool is_connected)
: this()
{
gamepad_type = (byte)type;
this.axes = axes;
this.buttons = buttons;
this.is_connected = is_connected;
}
#endregion
#region Public Members
/// <summary>
/// Gets a <see cref="GamePadType"/> value describing the type of a <see cref="GamePad"/> input device.
/// This value depends on the connected device and the drivers in use. If <c>IsConnected</c>
/// is false, then this value will be <c>GamePadType.Unknown</c>.
/// </summary>
/// <value>The <c>GamePadType</c> of the connected input device.</value>
public GamePadType GamePadType
{
get { return (GamePadType)gamepad_type; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// an up digital pad button.
/// </summary>
/// <value><c>true</c> if this instance has an up digital pad button; otherwise, <c>false</c>.</value>
public bool HasDPadUpButton
{
get { return (buttons & Buttons.DPadUp) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a down digital pad button.
/// </summary>
/// <value><c>true</c> if this instance has a down digital pad button; otherwise, <c>false</c>.</value>
public bool HasDPadDownButton
{
get { return (buttons & Buttons.DPadDown) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a left digital pad button.
/// </summary>
/// <value><c>true</c> if this instance has a left digital pad button; otherwise, <c>false</c>.</value>
public bool HasDPadLeftButton
{
get { return (buttons & Buttons.DPadLeft) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a right digital pad button.
/// </summary>
/// <value><c>true</c> if this instance has a right digital pad button; otherwise, <c>false</c>.</value>
public bool HasDPadRightButton
{
get { return (buttons & Buttons.DPadRight) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// an A button.
/// </summary>
/// <value><c>true</c> if this instance has an A button; otherwise, <c>false</c>.</value>
public bool HasAButton
{
get { return (buttons & Buttons.A) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a B button.
/// </summary>
/// <value><c>true</c> if this instance has a B button; otherwise, <c>false</c>.</value>
public bool HasBButton
{
get { return (buttons & Buttons.B) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a X button.
/// </summary>
/// <value><c>true</c> if this instance has a X button; otherwise, <c>false</c>.</value>
public bool HasXButton
{
get { return (buttons & Buttons.X) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a Y button.
/// </summary>
/// <value><c>true</c> if this instance has a Y button; otherwise, <c>false</c>.</value>
public bool HasYButton
{
get { return (buttons & Buttons.Y) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a left stick button.
/// </summary>
/// <value><c>true</c> if this instance has a left stick button; otherwise, <c>false</c>.</value>
public bool HasLeftStickButton
{
get { return (buttons & Buttons.LeftStick) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a right stick button.
/// </summary>
/// <value><c>true</c> if this instance has a right stick button; otherwise, <c>false</c>.</value>
public bool HasRightStickButton
{
get { return (buttons & Buttons.RightStick) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a left shoulder button.
/// </summary>
/// <value><c>true</c> if this instance has a left shoulder button; otherwise, <c>false</c>.</value>
public bool HasLeftShoulderButton
{
get { return (buttons & Buttons.LeftShoulder) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a right shoulder button.
/// </summary>
/// <value><c>true</c> if this instance has a right shoulder button; otherwise, <c>false</c>.</value>
public bool HasRightShoulderButton
{
get { return (buttons & Buttons.RightShoulder) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a back button.
/// </summary>
/// <value><c>true</c> if this instance has a back button; otherwise, <c>false</c>.</value>
public bool HasBackButton
{
get { return (buttons & Buttons.Back) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a big button. (also known as "guide" or "home" button).
/// </summary>
/// <value><c>true</c> if this instance has a big button; otherwise, <c>false</c>.</value>
public bool HasBigButton
{
get { return (buttons & Buttons.BigButton) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a start button.
/// </summary>
/// <value><c>true</c> if this instance has a start button; otherwise, <c>false</c>.</value>
public bool HasStartButton
{
get { return (buttons & Buttons.Start) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a left thumbstick with a x-axis.
/// </summary>
/// <value><c>true</c> if this instance has a left thumbstick with a x-axis; otherwise, <c>false</c>.</value>
public bool HasLeftXThumbStick
{
get { return (axes & GamePadAxes.LeftX) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a left thumbstick with a y-axis.
/// </summary>
/// <value><c>true</c> if this instance has a left thumbstick with a y-axis; otherwise, <c>false</c>.</value>
public bool HasLeftYThumbStick
{
get { return (axes & GamePadAxes.LeftY) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a right thumbstick with a x-axis.
/// </summary>
/// <value><c>true</c> if this instance has a right thumbstick with a x-axis; otherwise, <c>false</c>.</value>
public bool HasRightXThumbStick
{
get { return (axes & GamePadAxes.RightX) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a right thumbstick with a y-axis.
/// </summary>
/// <value><c>true</c> if this instance has a right thumbstick with a y-axis; otherwise, <c>false</c>.</value>
public bool HasRightYThumbStick
{
get { return (axes & GamePadAxes.RightY) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a left trigger.
/// </summary>
/// <value><c>true</c> if this instance has a left trigger; otherwise, <c>false</c>.</value>
public bool HasLeftTrigger
{
get { return (axes & GamePadAxes.LeftTrigger) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a right trigger.
/// </summary>
/// <value><c>true</c> if this instance has a right trigger; otherwise, <c>false</c>.</value>
public bool HasRightTrigger
{
get { return (axes & GamePadAxes.RightTrigger) != 0; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a low-frequency vibration motor.
/// </summary>
/// <value><c>true</c> if this instance has a low-frequency vibration motor; otherwise, <c>false</c>.</value>
public bool HasLeftVibrationMotor
{
get { return false; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a high-frequency vibration motor.
/// </summary>
/// <value><c>true</c> if this instance has a high frequency vibration motor; otherwise, <c>false</c>.</value>
public bool HasRightVibrationMotor
{
get { return false; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> has
/// a microphone input.
/// </summary>
/// <value><c>true</c> if this instance has a microphone input; otherwise, <c>false</c>.</value>
public bool HasVoiceSupport
{
get { return false; }
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> value describing whether this <c>GamePad</c> is
/// currently connected.
/// </summary>
/// <value><c>true</c> if this instance is currently connected; otherwise, <c>false</c>.</value>
public bool IsConnected
{
get { return is_connected; }
}
/// <param name="left">A <see cref="GamePadCapabilities"/> structure to test for equality.</param>
/// <param name="right">A <see cref="GamePadCapabilities"/> structure to test for equality.</param>
public static bool operator ==(GamePadCapabilities left, GamePadCapabilities right)
{
return left.Equals(right);
}
/// <param name="left">A <see cref="GamePadCapabilities"/> structure to test for inequality.</param>
/// <param name="right">A <see cref="GamePadCapabilities"/> structure to test for inequality.</param>
public static bool operator !=(GamePadCapabilities left, GamePadCapabilities right)
{
return !left.Equals(right);
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.GamePadCapabilities"/>.
/// </summary>
/// <returns>A <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.GamePadCapabilities"/>.</returns>
public override string ToString()
{
return String.Format(
"{{Type: {0}; Axes: {1}; Buttons: {2}; Connected: {3}}}",
GamePadType,
Convert.ToString((int)axes, 2),
Convert.ToString((int)buttons, 2),
IsConnected);
}
/// <summary>
/// Serves as a hash function for a <see cref="OpenTK.Input.GamePadCapabilities"/> object.
/// </summary>
/// <returns>A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
/// hash table.</returns>
public override int GetHashCode()
{
return
buttons.GetHashCode() ^
is_connected.GetHashCode() ^
gamepad_type.GetHashCode();
}
/// <summary>
/// Determines whether the specified <see cref="System.Object"/> is equal to the current <see cref="OpenTK.Input.GamePadCapabilities"/>.
/// </summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with the current <see cref="OpenTK.Input.GamePadCapabilities"/>.</param>
/// <returns><c>true</c> if the specified <see cref="System.Object"/> is equal to the current
/// <see cref="OpenTK.Input.GamePadCapabilities"/>; otherwise, <c>false</c>.</returns>
public override bool Equals(object obj)
{
return
obj is GamePadCapabilities &&
Equals((GamePadCapabilities)obj);
}
#endregion
#region IEquatable<GamePadCapabilities> Members
/// <summary>
/// Determines whether the specified <see cref="OpenTK.Input.GamePadCapabilities"/> is equal to the current <see cref="OpenTK.Input.GamePadCapabilities"/>.
/// </summary>
/// <param name="other">The <see cref="OpenTK.Input.GamePadCapabilities"/> to compare with the current <see cref="OpenTK.Input.GamePadCapabilities"/>.</param>
/// <returns><c>true</c> if the specified <see cref="OpenTK.Input.GamePadCapabilities"/> is equal to the current
/// <see cref="OpenTK.Input.GamePadCapabilities"/>; otherwise, <c>false</c>.</returns>
public bool Equals(GamePadCapabilities other)
{
return
buttons == other.buttons &&
is_connected == other.is_connected &&
gamepad_type == other.gamepad_type;
}
#endregion
}
}

View file

@ -0,0 +1,204 @@
#region License
//
// GamePadMapping.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-2013 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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace OpenTK.Input
{
sealed class GamePadConfiguration
{
static readonly char[] ConfigurationSeparator = new char[] { ',' };
Guid guid;
string name;
readonly List<GamePadConfigurationItem> configuration_items =
new List<GamePadConfigurationItem>();
public Guid Guid
{
get { return guid; }
private set { guid = value; }
}
public string Name
{
get { return name; }
private set { name = value; }
}
public GamePadConfiguration(string configuration)
{
ParseConfiguration(configuration);
}
public List<GamePadConfigurationItem>.Enumerator GetEnumerator()
{
return configuration_items.GetEnumerator();
}
#region Private Members
// Parses a GamePad configuration string. The string
// follows the rules for SDL2 GameController, outlined here:
// http://wiki.libsdl.org/SDL_GameControllerAddMapping
void ParseConfiguration(string configuration)
{
if (String.IsNullOrEmpty(configuration))
{
throw new ArgumentNullException();
}
// The mapping string has the format "GUID,name,config"
// - GUID is a unigue identifier returned by Joystick.GetGuid()
// - name is a human-readable name for the controller
// - config is a comma-separated list of configurations as follows:
// - [gamepad axis or button name]:[joystick axis, button or hat number]
string[] items = configuration.Split(ConfigurationSeparator, StringSplitOptions.RemoveEmptyEntries);
if (items.Length < 3)
{
throw new ArgumentException();
}
GamePadConfiguration map = this;
map.Guid = new Guid(items[0]);
map.Name = items[1];
for (int i = 2; i < items.Length; i++)
{
string[] config = items[i].Split(':');
GamePadConfigurationTarget target = ParseTarget(config[0]);
GamePadConfigurationSource source = ParseSource(config[1]);
configuration_items.Add(new GamePadConfigurationItem(source, target));
}
}
static GamePadConfigurationTarget ParseTarget(string target)
{
switch (target)
{
// Buttons
case "a":
return new GamePadConfigurationTarget(Buttons.A);
case "b":
return new GamePadConfigurationTarget(Buttons.B);
case "x":
return new GamePadConfigurationTarget(Buttons.X);
case "y":
return new GamePadConfigurationTarget(Buttons.Y);
case "start":
return new GamePadConfigurationTarget(Buttons.Start);
case "back":
return new GamePadConfigurationTarget(Buttons.Back);
case "guide":
return new GamePadConfigurationTarget(Buttons.BigButton);
case "leftshoulder":
return new GamePadConfigurationTarget(Buttons.LeftShoulder);
case "rightshoulder":
return new GamePadConfigurationTarget(Buttons.RightShoulder);
case "leftstick":
return new GamePadConfigurationTarget(Buttons.LeftStick);
case "rightstick":
return new GamePadConfigurationTarget(Buttons.RightStick);
case "dpup":
return new GamePadConfigurationTarget(Buttons.DPadUp);
case "dpdown":
return new GamePadConfigurationTarget(Buttons.DPadDown);
case "dpleft":
return new GamePadConfigurationTarget(Buttons.DPadLeft);
case "dpright":
return new GamePadConfigurationTarget(Buttons.DPadRight);
// Axes
case "leftx":
return new GamePadConfigurationTarget(GamePadAxes.LeftX);
case "lefty":
return new GamePadConfigurationTarget(GamePadAxes.LeftY);
case "rightx":
return new GamePadConfigurationTarget(GamePadAxes.RightX);
case "righty":
return new GamePadConfigurationTarget(GamePadAxes.RightY);
// Triggers
case "lefttrigger":
return new GamePadConfigurationTarget(GamePadAxes.LeftTrigger);
case "righttrigger":
return new GamePadConfigurationTarget(GamePadAxes.RightTrigger);
// Unmapped
default:
return new GamePadConfigurationTarget();
}
}
static GamePadConfigurationSource ParseSource(string item)
{
if (String.IsNullOrEmpty(item))
{
return new GamePadConfigurationSource();
}
switch (item[0])
{
case 'a':
return new GamePadConfigurationSource(ParseAxis(item));
case 'b':
return new GamePadConfigurationSource(ParseButton(item));
case 'h':
throw new NotImplementedException();
//return new MapItem(ParseHat(item));
default:
throw new InvalidOperationException("[Input] Invalid GamePad configuration value");
}
}
static JoystickAxis ParseAxis(string item)
{
// item is in the format "a#" where # a zero-based integer number
JoystickAxis axis = JoystickAxis.Axis0;
int id = Int32.Parse(item.Substring(1));
return axis + id;
}
static JoystickButton ParseButton(string item)
{
// item is in the format "b#" where # a zero-based integer nubmer
JoystickButton button = JoystickButton.Button0;
int id = Int32.Parse(item.Substring(1));
return button + id;
}
#endregion
}
}

View file

@ -0,0 +1,132 @@
#region License
//
// GamePadConfigurationDatabase.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;
using System.Collections.Generic;
namespace OpenTK.Input
{
class GamePadConfigurationDatabase
{
readonly Dictionary<Guid, string> Configurations = new Dictionary<Guid, string>();
internal GamePadConfigurationDatabase()
{
// Configuration database copied from SDL
#region License
// Simple DirectMedia Layer
// Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#endregion
// Default configuration, used when no suitable configuration is available.
// Add("00000000000000000000000000000000,Unknown Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,");
// Valid for all xinput devices on Windows:
Add("00000000000000000000000000000000,X360 Controller,a:b10,b:b11,back:b5,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,guide:b14,leftshoulder:b8,leftstick:b6,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b4,x:b12,y:b13,");
// Windows
Add("341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,");
Add("ffff0000000000000000504944564944,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,");
Add("6d0416c2000000000000504944564944,Generic DirectInput Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,");
Add("6d0419c2000000000000504944564944,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); // Guide button doesn't seem to be sent in DInput mode.
Add("88880803000000000000504944564944,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,");
Add("4c056802000000000000504944564944,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,");
Add("25090500000000000000504944564944,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,");
// Mac OS X
Add("0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,");
Add("6d0400000000000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); /* Guide button doesn't seem to be sent in DInput mode. */
Add("6d0400000000000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,");
Add("6d040000000000001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,");
Add("6d0400000000000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); /* This includes F710 in DInput mode and the "Logitech Cordless RumblePad 2"); at the very least. */
Add("4c050000000000006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,");
Add("5e040000000000008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,");
// Linux
Add("0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,");
Add("03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,");
Add("030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,");
Add("030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,");
Add("030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,");
Add("030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); /* Guide button doesn't seem to be sent in DInput mode. */
Add("030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,");
Add("030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,");
Add("03000000de280000ff11000001000000,Valve Streaming Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,");
Add("030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,");
Add("030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,");
Add("030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,");
Add("030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,");
}
internal void Add(string config)
{
Guid guid = new Guid(config.Substring(0, 32));
if (!Configurations.ContainsKey(guid))
{
Configurations.Add(guid, config);
}
else
{
Configurations[guid] = config;
}
}
internal string this[Guid guid]
{
get
{
if (Configurations.ContainsKey(guid))
{
return Configurations[guid];
}
else
{
return Configurations[new Guid()]; // default configuration
}
}
}
}
}

View file

@ -0,0 +1,58 @@
#region License
//
// GamePadConfigurationItem.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.Input
{
class GamePadConfigurationItem
{
GamePadConfigurationSource source;
GamePadConfigurationTarget target;
public GamePadConfigurationItem(GamePadConfigurationSource source, GamePadConfigurationTarget target)
{
Source = source;
Target = target;
}
public GamePadConfigurationSource Source
{
get { return source; }
private set { source = value; }
}
public GamePadConfigurationTarget Target
{
get { return target; }
private set { target = value; }
}
}
}

View file

@ -0,0 +1,72 @@
#region License
//
// GamePadConfigurationItem.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.Input
{
struct GamePadConfigurationSource
{
ConfigurationType map_type;
Nullable<JoystickButton> map_button;
Nullable<JoystickAxis> map_axis;
public GamePadConfigurationSource(JoystickAxis axis)
: this()
{
Type = ConfigurationType.Axis;
Axis = axis;
}
public GamePadConfigurationSource(JoystickButton button)
: this()
{
Type = ConfigurationType.Button;
Button = button;
}
public ConfigurationType Type
{
get { return map_type; }
private set { map_type = value; }
}
public JoystickAxis Axis
{
get { return map_axis.Value; }
private set { map_axis = value; }
}
public JoystickButton Button
{
get { return map_button.Value; }
private set { map_button = value; }
}
}
}

View file

@ -0,0 +1,72 @@
#region License
//
// GamePadConfigurationTarget.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.Input
{
struct GamePadConfigurationTarget
{
ConfigurationType map_type;
Nullable<Buttons> map_button;
Nullable<GamePadAxes> map_axis;
public GamePadConfigurationTarget(Buttons button)
: this()
{
Type = ConfigurationType.Button;
map_button = button;
}
public GamePadConfigurationTarget(GamePadAxes axis)
: this()
{
Type = ConfigurationType.Axis;
map_axis = axis;
}
public ConfigurationType Type
{
get { return map_type; }
private set { map_type = value; }
}
public GamePadAxes Axis
{
get { return map_axis.Value; }
private set { map_axis = value; }
}
public Buttons Button
{
get { return map_button.Value; }
private set { map_button = value; }
}
}
}

View file

@ -0,0 +1,223 @@
// #region License
//
// GamePadDPad.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-2013 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.Input
{
/// <summary>
/// Describes the state of a <see cref="GamePad"/> directional pad.
/// </summary>
public struct GamePadDPad : IEquatable<GamePadDPad>
{
[Flags]
enum DPadButtons : byte
{
Up = Buttons.DPadUp,
Down = Buttons.DPadDown,
Left = Buttons.DPadLeft,
Right = Buttons.DPadRight
}
DPadButtons buttons;
#region Internal Members
internal GamePadDPad(Buttons state)
{
// DPad butons are stored in the lower 4bits
// of the Buttons enumeration.
buttons = (DPadButtons)((int)state & 0x0f);
}
#endregion
#region Public Members
/// <summary>
/// Gets the <see cref="ButtonState"/> for the up button.
/// </summary>
/// <value><c>ButtonState.Pressed</c> if the up button is pressed; otherwise, <c>ButtonState.Released</c>.</value>
public ButtonState Up
{
get { return IsUp ? ButtonState.Pressed : ButtonState.Released; }
}
/// <summary>
/// Gets the <see cref="ButtonState"/> for the down button.
/// </summary>
/// <value><c>ButtonState.Pressed</c> if the down button is pressed; otherwise, <c>ButtonState.Released</c>.</value>
public ButtonState Down
{
get { return IsDown ? ButtonState.Pressed : ButtonState.Released; }
}
/// <summary>
/// Gets the <see cref="ButtonState"/> for the left button.
/// </summary>
/// <value><c>ButtonState.Pressed</c> if the left button is pressed; otherwise, <c>ButtonState.Released</c>.</value>
public ButtonState Left
{
get { return IsLeft ? ButtonState.Pressed : ButtonState.Released; }
}
/// <summary>
/// Gets the <see cref="ButtonState"/> for the right button.
/// </summary>
/// <value><c>ButtonState.Pressed</c> if the right button is pressed; otherwise, <c>ButtonState.Released</c>.</value>
public ButtonState Right
{
get { return IsRight ? ButtonState.Pressed : ButtonState.Released; }
}
/// <summary>
/// Gets a value indicating whether the up button is pressed.
/// </summary>
/// <value><c>true</c> if the up button is pressed; otherwise, <c>false</c>.</value>
public bool IsUp
{
get { return (buttons & DPadButtons.Up) != 0; }
internal set { SetButton(DPadButtons.Up, value); }
}
/// <summary>
/// Gets a value indicating whether the down button is pressed.
/// </summary>
/// <value><c>true</c> if the down button is pressed; otherwise, <c>false</c>.</value>
public bool IsDown
{
get { return (buttons & DPadButtons.Down) != 0; }
internal set { SetButton(DPadButtons.Down, value); }
}
/// <summary>
/// Gets a value indicating whether the left button is pressed.
/// </summary>
/// <value><c>true</c> if the left button is pressed; otherwise, <c>false</c>.</value>
public bool IsLeft
{
get { return (buttons & DPadButtons.Left) != 0; }
internal set { SetButton(DPadButtons.Left, value); }
}
/// <summary>
/// Gets a value indicating whether the right button is pressed.
/// </summary>
/// <value><c>true</c> if the right button is pressed; otherwise, <c>false</c>.</value>
public bool IsRight
{
get { return (buttons & DPadButtons.Right) != 0; }
internal set { SetButton(DPadButtons.Right, value); }
}
/// <param name="left">A <see cref="GamePadDPad"/> instance to test for equality.</param>
/// <param name="right">A <see cref="GamePadDPad"/> instance to test for equality.</param>
public static bool operator ==(GamePadDPad left, GamePadDPad right)
{
return left.Equals(right);
}
/// <param name="left">A <see cref="GamePadDPad"/> instance to test for inequality.</param>
/// <param name="right">A <see cref="GamePadDPad"/> instance to test for inequality.</param>
public static bool operator !=(GamePadDPad left, GamePadDPad right)
{
return !left.Equals(right);
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.GamePadDPad"/>.
/// </summary>
/// <returns>A <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.GamePadDPad"/>.</returns>
public override string ToString()
{
return String.Format(
"{{{0}{1}{2}{3}}}",
IsUp ? "U" : String.Empty,
IsLeft ? "L" : String.Empty,
IsDown ? "D" : String.Empty,
IsRight ? "R" : String.Empty);
}
/// <summary>
/// Serves as a hash function for a <see cref="OpenTK.Input.GamePadDPad"/> object.
/// </summary>
/// <returns>A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
/// hash table.</returns>
public override int GetHashCode()
{
return buttons.GetHashCode();
}
/// <summary>
/// Determines whether the specified <see cref="System.Object"/> is equal to the current <see cref="OpenTK.Input.GamePadDPad"/>.
/// </summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with the current <see cref="OpenTK.Input.GamePadDPad"/>.</param>
/// <returns><c>true</c> if the specified <see cref="System.Object"/> is equal to the current
/// <see cref="OpenTK.Input.GamePadDPad"/>; otherwise, <c>false</c>.</returns>
public override bool Equals(object obj)
{
return
obj is GamePadDPad &&
Equals((GamePadDPad)obj);
}
#endregion
#region Private Members
void SetButton(DPadButtons button, bool value)
{
if (value)
{
buttons |= button;
}
else
{
buttons &= ~button;
}
}
#endregion
#region IEquatable<GamePadDPad> Members
/// <summary>
/// Determines whether the specified <see cref="OpenTK.Input.GamePadDPad"/> is equal to the current <see cref="OpenTK.Input.GamePadDPad"/>.
/// </summary>
/// <param name="other">The <see cref="OpenTK.Input.GamePadDPad"/> to compare with the current <see cref="OpenTK.Input.GamePadDPad"/>.</param>
/// <returns><c>true</c> if the specified <see cref="OpenTK.Input.GamePadDPad"/> is equal to the current
/// <see cref="OpenTK.Input.GamePadDPad"/>; otherwise, <c>false</c>.</returns>
public bool Equals(GamePadDPad other)
{
return buttons == other.buttons;
}
#endregion
}
}

View file

@ -30,10 +30,216 @@ using System;
namespace OpenTK.Input namespace OpenTK.Input
{ {
/// <summary> /// <summary>
/// Encapsulates the state of a GamePad device. /// Describes the current state of a <see cref="GamePad"/> device.
/// </summary> /// </summary>
public struct GamePadState /*: IEquatable<GamePadState>*/ public struct GamePadState : IEquatable<GamePadState>
{ {
const float RangeMultiplier = 1.0f / (short.MaxValue + 1);
Buttons buttons;
int packet_number;
short left_stick_x;
short left_stick_y;
short right_stick_x;
short right_stick_y;
byte left_trigger;
byte right_trigger;
bool is_connected;
#region Public Members
/// <summary>
/// Gets a <see cref="GamePadThumbSticks"/> structure describing the
/// state of the <c>GamePad</c> thumb sticks.
/// </summary>
public GamePadThumbSticks ThumbSticks
{
get { return new GamePadThumbSticks(left_stick_x, left_stick_y, right_stick_x, right_stick_y); }
}
/// <summary>
/// Gets a <see cref="GamePadButtons"/> structure describing the
/// state of the <c>GamePad</c> buttons.
/// </summary>
public GamePadButtons Buttons
{
get { return new GamePadButtons(buttons); }
}
/// <summary>
/// Gets a <see cref="GamePadDPad"/> structure describing the
/// state of the <c>GamePad</c> directional pad.
/// </summary>
public GamePadDPad DPad
{
get { return new GamePadDPad(buttons); }
}
/// <summary>
/// Gets a <see cref="GamePadTriggers"/> structure describing the
/// state of the <c>GamePad</c> triggers.
/// </summary>
public GamePadTriggers Triggers
{
get { return new GamePadTriggers(left_trigger, right_trigger); }
}
/// <summary>
/// Gets a value indicating whether this <c>GamePad</c> instance is connected.
/// </summary>
/// <value><c>true</c> if this instance is connected; otherwise, <c>false</c>.</value>
public bool IsConnected
{
get { return is_connected; }
}
/// <summary>
/// Gets the packet number for this <c>GamePadState</c> instance.
/// Use the packet number to determine whether the state of a
/// <c>GamePad</c> device has changed.
/// </summary>
public int PacketNumber
{
get { return packet_number; }
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.GamePadState"/>.
/// </summary>
/// <returns>A <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.GamePadState"/>.</returns>
public override string ToString()
{
return String.Format(
"{{Sticks: {0}; Buttons: {1}; DPad: {2}; IsConnected: {3}}}",
ThumbSticks, Buttons, DPad, IsConnected);
}
/// <summary>
/// Serves as a hash function for a <see cref="OpenTK.Input.GamePadState"/> object.
/// </summary>
/// <returns>A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
/// hash table.</returns>
public override int GetHashCode()
{
return
ThumbSticks.GetHashCode() ^ Buttons.GetHashCode() ^
DPad.GetHashCode() ^ IsConnected.GetHashCode();
}
/// <summary>
/// Determines whether the specified <see cref="System.Object"/> is equal to the current <see cref="OpenTK.Input.GamePadState"/>.
/// </summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with the current <see cref="OpenTK.Input.GamePadState"/>.</param>
/// <returns><c>true</c> if the specified <see cref="System.Object"/> is equal to the current
/// <see cref="OpenTK.Input.GamePadState"/>; otherwise, <c>false</c>.</returns>
public override bool Equals(object obj)
{
return
obj is GamePadState &&
Equals((GamePadState)obj);
}
#endregion
#region IEquatable<GamePadState> Members
/// <summary>
/// Determines whether the specified <see cref="OpenTK.Input.GamePadState"/> is equal to the current <see cref="OpenTK.Input.GamePadState"/>.
/// </summary>
/// <param name="other">The <see cref="OpenTK.Input.GamePadState"/> to compare with the current <see cref="OpenTK.Input.GamePadState"/>.</param>
/// <returns><c>true</c> if the specified <see cref="OpenTK.Input.GamePadState"/> is equal to the current
/// <see cref="OpenTK.Input.GamePadState"/>; otherwise, <c>false</c>.</returns>
public bool Equals(GamePadState other)
{
return
ThumbSticks == other.ThumbSticks &&
Buttons == other.Buttons &&
DPad == other.DPad &&
IsConnected == other.IsConnected;
}
#endregion
#region Internal Members
internal void SetAxis(GamePadAxes axis, short value)
{
if ((axis & GamePadAxes.LeftX) != 0)
{
left_stick_x = value;
}
if ((axis & GamePadAxes.LeftY) != 0)
{
left_stick_y = value;
}
if ((axis & GamePadAxes.RightX) != 0)
{
right_stick_x = value;
}
if ((axis & GamePadAxes.RightY) != 0)
{
right_stick_y = value;
}
if ((axis & GamePadAxes.LeftTrigger) != 0)
{
// Adjust from [-32768, 32767] to [0, 255]
left_trigger = (byte)((value - short.MinValue) >> 8);
}
if ((axis & GamePadAxes.RightTrigger) != 0)
{
// Adjust from [-32768, 32767] to [0, 255]
right_trigger = (byte)((value - short.MinValue) >> 8);
}
}
internal void SetButton(Buttons button, bool pressed)
{
if (pressed)
{
buttons |= button;
}
else
{
buttons &= ~button;
}
}
internal void SetConnected(bool connected)
{
is_connected = connected;
}
internal void SetTriggers(byte left, byte right)
{
left_trigger = left;
right_trigger = right;
}
internal void SetPacketNumber(int number)
{
packet_number = number;
}
#endregion
#region Private Members
bool IsAxisValid(GamePadAxes axis)
{
int index = (int)axis;
return index >= 0 && index < GamePad.MaxAxisCount;
}
bool IsDPadValid(int index)
{
return index >= 0 && index < GamePad.MaxDPadCount;
}
#endregion
} }
} }

View file

@ -0,0 +1,142 @@
#region License
//
// GamePadThumbSticks.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-2013 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.Input
{
/// <summary>
/// Describes the current thumb stick state of a <see cref="GamePad"/> device
/// </summary>
public struct GamePadThumbSticks : IEquatable<GamePadThumbSticks>
{
const float ConversionFactor = 1.0f / short.MaxValue;
short left_x, left_y;
short right_x, right_y;
internal GamePadThumbSticks(
short left_x, short left_y,
short right_x, short right_y)
{
this.left_x = left_x;
this.left_y = left_y;
this.right_x = right_x;
this.right_y = right_y;
}
#region Public Members
/// <summary>
/// Gets a <see cref="Vector2"/> describing the state of the left thumb stick.
/// </summary>
public Vector2 Left
{
get { return new Vector2(left_x * ConversionFactor, left_y * ConversionFactor); }
}
/// <summary>
/// Gets a <see cref="Vector2"/> describing the state of the right thumb stick.
/// </summary>
public Vector2 Right
{
get { return new Vector2(right_x * ConversionFactor, right_y * ConversionFactor); }
}
/// <param name="left">A <see cref="GamePadThumbSticks"/> instance to test for equality.</param>
/// <param name="right">A <see cref="GamePadThumbSticks"/> instance to test for equality.</param>
public static bool operator ==(GamePadThumbSticks left, GamePadThumbSticks right)
{
return left.Equals(right);
}
/// <param name="left">A <see cref="GamePadThumbSticks"/> instance to test for inequality.</param>
/// <param name="right">A <see cref="GamePadThumbSticks"/> instance to test for inequality.</param>
public static bool operator !=(GamePadThumbSticks left, GamePadThumbSticks right)
{
return !left.Equals(right);
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.GamePadThumbSticks"/>.
/// </summary>
/// <returns>A <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.GamePadThumbSticks"/>.</returns>
public override string ToString()
{
return String.Format(
"{{Left: ({0:f4}; {1:f4}); Right: ({2:f4}; {3:f4})}}",
Left.X, Left.Y, Right.X, Right.Y);
}
/// <summary>
/// Serves as a hash function for a <see cref="OpenTK.Input.GamePadThumbSticks"/> object.
/// </summary>
/// <returns>A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
/// hash table.</returns>
public override int GetHashCode()
{
return
left_x.GetHashCode() ^ left_y.GetHashCode() ^
right_x.GetHashCode() ^ right_y.GetHashCode();
}
/// <summary>
/// Determines whether the specified <see cref="System.Object"/> is equal to the current <see cref="OpenTK.Input.GamePadThumbSticks"/>.
/// </summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with the current <see cref="OpenTK.Input.GamePadThumbSticks"/>.</param>
/// <returns><c>true</c> if the specified <see cref="System.Object"/> is equal to the current
/// <see cref="OpenTK.Input.GamePadThumbSticks"/>; otherwise, <c>false</c>.</returns>
public override bool Equals(object obj)
{
return
obj is GamePadThumbSticks &&
Equals((GamePadThumbSticks)obj);
}
#endregion
#region IEquatable<GamePadThumbSticks> Members
/// <summary>
/// Determines whether the specified <see cref="OpenTK.Input.GamePadThumbSticks"/> is equal to the current <see cref="OpenTK.Input.GamePadThumbSticks"/>.
/// </summary>
/// <param name="other">The <see cref="OpenTK.Input.GamePadThumbSticks"/> to compare with the current <see cref="OpenTK.Input.GamePadThumbSticks"/>.</param>
/// <returns><c>true</c> if the specified <see cref="OpenTK.Input.GamePadThumbSticks"/> is equal to the current
/// <see cref="OpenTK.Input.GamePadThumbSticks"/>; otherwise, <c>false</c>.</returns>
public bool Equals(GamePadThumbSticks other)
{
return
left_x == other.left_x &&
left_y == other.left_y &&
right_x == other.right_x &&
right_y == other.right_y;
}
#endregion
}
}

View file

@ -0,0 +1,136 @@
// #region License
//
// GamePadTriggers.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-2013 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.Input
{
/// <summary>
/// Describes the state of a <see cref="GamePad"/> trigger buttons.
/// </summary>
public struct GamePadTriggers : IEquatable<GamePadTriggers>
{
const float ConversionFactor = 1.0f / short.MaxValue;
short left;
short right;
internal GamePadTriggers(short left, short right)
{
this.left = left;
this.right = right;
}
#region Public Members
/// <summary>
/// Gets the offset of the left trigger button, between 0.0 and 1.0.
/// </summary>
public float Left
{
get { return left * ConversionFactor; }
}
/// <summary>
/// Gets the offset of the left trigger button, between 0.0 and 1.0.
/// </summary>
public float Right
{
get { return right * ConversionFactor; }
}
/// <param name="left">A <see cref="GamePadTriggers"/> instance to test for equality.</param>
/// <param name="right">A <see cref="GamePadTriggers"/> instance to test for equality.</param>
public static bool operator ==(GamePadTriggers left, GamePadTriggers right)
{
return left.Equals(right);
}
/// <param name="left">A <see cref="GamePadTriggers"/> instance to test for equality.</param>
/// <param name="right">A <see cref="GamePadTriggers"/> instance to test for equality.</param>
public static bool operator !=(GamePadTriggers left, GamePadTriggers right)
{
return !left.Equals(right);
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.GamePadTriggers"/>.
/// </summary>
/// <returns>A <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.GamePadTriggers"/>.</returns>
public override string ToString()
{
return String.Format(
"{{Left: {0:f2}; Right: {1:f2}}}",
Left, Right);
}
/// <summary>
/// Serves as a hash function for a <see cref="OpenTK.Input.GamePadTriggers"/> object.
/// </summary>
/// <returns>A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
/// hash table.</returns>
public override int GetHashCode()
{
return
left.GetHashCode() ^ right.GetHashCode();
}
/// <summary>
/// Determines whether the specified <see cref="System.Object"/> is equal to the current <see cref="OpenTK.Input.GamePadTriggers"/>.
/// </summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with the current <see cref="OpenTK.Input.GamePadTriggers"/>.</param>
/// <returns><c>true</c> if the specified <see cref="System.Object"/> is equal to the current
/// <see cref="OpenTK.Input.GamePadTriggers"/>; otherwise, <c>false</c>.</returns>
public override bool Equals(object obj)
{
return
obj is GamePadTriggers &&
Equals((GamePadTriggers)obj);
}
#endregion
#region IEquatable<GamePadTriggers> Members
/// <summary>
/// Determines whether the specified <see cref="OpenTK.Input.GamePadTriggers"/> is equal to the current <see cref="OpenTK.Input.GamePadTriggers"/>.
/// </summary>
/// <param name="other">The <see cref="OpenTK.Input.GamePadTriggers"/> to compare with the current <see cref="OpenTK.Input.GamePadTriggers"/>.</param>
/// <returns><c>true</c> if the specified <see cref="OpenTK.Input.GamePadTriggers"/> is equal to the current
/// <see cref="OpenTK.Input.GamePadTriggers"/>; otherwise, <c>false</c>.</returns>
public bool Equals(GamePadTriggers other)
{
return
left == other.left &&
right == other.right;
}
#endregion
}
}

View file

@ -0,0 +1,97 @@
#region License
//
// GamePadType.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-2013 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
namespace OpenTK.Input
{
/// <summary>
/// Enumerates available <see cref="GamePad"/> types.
/// </summary>
public enum GamePadType
{
/// <summary>
/// The <c>GamePad</c> is of an unknown type.
/// </summary>
Unknown = 0,
/// <summary>
/// The <c>GamePad</c> is an arcade stick.
/// </summary>
ArcadeStick,
/// <summary>
/// The <c>GamePad</c> is a dance pad.
/// </summary>
DancePad,
/// <summary>
/// The <c>GamePad</c> is a flight stick.
/// </summary>
FlightStick,
/// <summary>
/// The <c>GamePad</c> is a guitar.
/// </summary>
Guitar,
/// <summary>
/// The <c>GamePad</c> is a driving wheel.
/// </summary>
Wheel,
/// <summary>
/// The <c>GamePad</c> is an alternate guitar.
/// </summary>
AlternateGuitar,
/// <summary>
/// The <c>GamePad</c> is a big button pad.
/// </summary>
BigButtonPad,
/// <summary>
/// The <c>GamePad</c> is a drum kit.
/// </summary>
DrumKit,
/// <summary>
/// The <c>GamePad</c> is a game pad.
/// </summary>
GamePad,
/// <summary>
/// The <c>GamePad</c> is an arcade pad.
/// </summary>
ArcadePad,
/// <summary>
/// The <c>GamePad</c> is a bass guitar.
/// </summary>
BassGuitar,
}
}

View file

@ -6,19 +6,11 @@ namespace OpenTK.Input
{ {
interface IGamePadDriver interface IGamePadDriver
{ {
/// <summary>
/// Retrieves the combined <see cref="OpenTK.Input.GamePadState"/> for all gamepad devices.
/// </summary>
/// <returns>A <see cref="OpenTK.Input.GamePadState"/> structure containing the combined state for all gamepad devices.</returns>
GamePadState GetState();
/// <summary>
/// Retrieves the <see cref="OpenTK.Input.GamePadState"/> for the specified gamepad device.
/// </summary>
/// <param name="index">The index of the keyboard device.</param>
/// <returns>A <see cref="OpenTK.Input.GamePadState"/> structure containing the state of the gamepad device.</returns>
GamePadState GetState(int index); GamePadState GetState(int index);
GamePadCapabilities GetCapabilities(int index);
/// <summary> /// <summary>
/// Retrieves the device name for the gamepad device. /// Retrieves the device name for the gamepad device.
/// </summary> /// </summary>
@ -26,6 +18,8 @@ namespace OpenTK.Input
/// <returns>A <see cref="System.String"/> with the name of the specified device or <see cref="System.String.Empty"/>.</returns> /// <returns>A <see cref="System.String"/> with the name of the specified device or <see cref="System.String.Empty"/>.</returns>
/// <remarks> /// <remarks>
/// <para>If no device exists at the specified index, the return value is <see cref="System.String.Empty"/>.</para></remarks> /// <para>If no device exists at the specified index, the return value is <see cref="System.String.Empty"/>.</para></remarks>
string GetDeviceName(int index); string GetName(int index);
bool SetVibration(int index, float left, float right);
} }
} }

View file

@ -37,5 +37,6 @@ namespace OpenTK.Input
IMouseDriver2 MouseDriver { get; } IMouseDriver2 MouseDriver { get; }
IKeyboardDriver2 KeyboardDriver { get; } IKeyboardDriver2 KeyboardDriver { get; }
IGamePadDriver GamePadDriver { get; } IGamePadDriver GamePadDriver { get; }
IJoystickDriver2 JoystickDriver { get; }
} }
} }

View file

@ -0,0 +1,42 @@
#region License
//
// IJoystickDriver2.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-2013 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;
using System.Collections.Generic;
using System.Text;
namespace OpenTK.Input
{
interface IJoystickDriver2
{
JoystickState GetState(int index);
JoystickCapabilities GetCapabilities(int index);
Guid GetGuid(int index);
}
}

View file

@ -0,0 +1,92 @@
#region License
//
// Joystick.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-2013 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;
using System.Collections.Generic;
using System.Text;
namespace OpenTK.Input
{
/// <summary>
/// Provides access to Joystick devices.
/// Joystick devices provide a varying number of axes and buttons.
/// Use <c>GetCapabilities</c> to retrieve the number of supported
/// axes and buttons on a given device.
/// Use <c>GetState</c> to retrieve the current state of a given device.
/// <seealso cref="GamePad"/>
/// </summary>
public sealed class Joystick
{
static readonly IJoystickDriver2 implementation =
Platform.Factory.Default.CreateJoystickDriver();
private Joystick() { }
/// <summary>
/// Retrieves the <see cref="JoystickCapabilities"/> of the device connected
/// at the specified index.
/// </summary>
/// <returns>
/// A <see cref="JoystickCapabilities"/> structure describing
/// the capabilities of the device at the specified index.
/// If no device is connected at the specified index, the <c>IsConnected</c>
/// property of the returned structure will be false.
/// </returns>
/// <param name="index">The zero-based index of the device to poll.</param>
public static JoystickCapabilities GetCapabilities(int index)
{
return implementation.GetCapabilities(index);
}
/// <summary>
/// Retrieves the <see cref="JoystickState"/> of the device connected
/// at the specified index.
/// </summary>
/// <returns>A <see cref="JoystickState"/> structure describing
/// the current state of the device at the specified index.
/// If no device is connected at this index, the <c>IsConnected</c>
/// property of the returned structure will be false.
/// </returns>
/// <param name="index">The zero-based index of the device to poll.</param>
public static JoystickState GetState(int index)
{
return implementation.GetState(index);
}
internal static Guid GetGuid(int index)
{
return implementation.GetGuid(index);
}
//public string GetName(int index)
//{
// return implementation.GetName(index);
//}
}
}

View file

@ -0,0 +1,66 @@
#region License
//
// JoystickAxis.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;
using System.Collections.Generic;
using System.Text;
namespace OpenTK.Input
{
/// <summary>
/// Defines available JoystickDevice axes.
/// </summary>
public enum JoystickAxis
{
/// <summary>The first axis of the JoystickDevice.</summary>
Axis0 = 0,
/// <summary>The second axis of the JoystickDevice.</summary>
Axis1,
/// <summary>The third axis of the JoystickDevice.</summary>
Axis2,
/// <summary>The fourth axis of the JoystickDevice.</summary>
Axis3,
/// <summary>The fifth axis of the JoystickDevice.</summary>
Axis4,
/// <summary>The sixth axis of the JoystickDevice.</summary>
Axis5,
/// <summary>The seventh axis of the JoystickDevice.</summary>
Axis6,
/// <summary>The eighth axis of the JoystickDevice.</summary>
Axis7,
/// <summary>The ninth axis of the JoystickDevice.</summary>
Axis8,
/// <summary>The tenth axis of the JoystickDevice.</summary>
Axis9,
/// <summary>The eleventh axis of the JoystickDevice.</summary>
Axis10,
/// <summary>The highest supported axis of the JoystickDevice.</summary>
Last = Axis10,
}
}

View file

@ -0,0 +1,76 @@
#region License
//
// JoystickButton.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-2013 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;
using System.Collections.Generic;
using System.Text;
namespace OpenTK.Input
{
/// <summary>
/// Defines available JoystickDevice buttons.
/// </summary>
public enum JoystickButton
{
/// <summary>The first button of the JoystickDevice.</summary>
Button0 = 0,
/// <summary>The second button of the JoystickDevice.</summary>
Button1,
/// <summary>The third button of the JoystickDevice.</summary>
Button2,
/// <summary>The fourth button of the JoystickDevice.</summary>
Button3,
/// <summary>The fifth button of the JoystickDevice.</summary>
Button4,
/// <summary>The sixth button of the JoystickDevice.</summary>
Button5,
/// <summary>The seventh button of the JoystickDevice.</summary>
Button6,
/// <summary>The eighth button of the JoystickDevice.</summary>
Button7,
/// <summary>The ninth button of the JoystickDevice.</summary>
Button8,
/// <summary>The tenth button of the JoystickDevice.</summary>
Button9,
/// <summary>The eleventh button of the JoystickDevice.</summary>
Button10,
/// <summary>The twelfth button of the JoystickDevice.</summary>
Button11,
/// <summary>The thirteenth button of the JoystickDevice.</summary>
Button12,
/// <summary>The fourteenth button of the JoystickDevice.</summary>
Button13,
/// <summary>The fifteenth button of the JoystickDevice.</summary>
Button14,
/// <summary>The sixteenth button of the JoystickDevice.</summary>
Button15,
/// <summary>The last supported button of the JoystickDevice.</summary>
Last = Button15,
}
}

View file

@ -0,0 +1,156 @@
#region License
//
// JoystickCapabilities.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-2013 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;
using System.Collections.Generic;
using System.Text;
namespace OpenTK.Input
{
/// <summary>
/// Describes the <c>JoystickCapabilities</c> of a <see cref="JoystickDevice"/>.
/// </summary>
public struct JoystickCapabilities : IEquatable<JoystickCapabilities>
{
byte axis_count;
byte button_count;
byte dpad_count;
bool is_connected;
#region Constructors
internal JoystickCapabilities(int axis_count, int button_count, bool is_connected)
{
if (axis_count < 0 || axis_count >= JoystickState.MaxAxes)
throw new ArgumentOutOfRangeException("axis_count");
if (button_count < 0 || button_count >= JoystickState.MaxButtons)
throw new ArgumentOutOfRangeException("axis_count");
this.axis_count = (byte)axis_count;
this.button_count = (byte)button_count;
this.dpad_count = 0; // Todo: either remove dpad_count or add it as a parameter
this.is_connected = is_connected;
}
#endregion
#region Public Members
/// <summary>
/// Gets the number of axes supported by this <see cref="JoystickDevice"/>.
/// </summary>
public int AxisCount
{
get { return axis_count; }
}
/// <summary>
/// Gets the number of buttons supported by this <see cref="JoystickDevice"/>.
/// </summary>
public int ButtonCount
{
get { return button_count; }
}
/// <summary>
/// Gets a value indicating whether this <see cref="JoystickDevice"/> is connected.
/// </summary>
/// <value><c>true</c> if this instance is connected; otherwise, <c>false</c>.</value>
public bool IsConnected
{
get { return is_connected; }
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.JoystickCapabilities"/>.
/// </summary>
/// <returns>A <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.JoystickCapabilities"/>.</returns>
public override string ToString()
{
return String.Format(
"{{Axes: {0}; Buttons: {1}; IsConnected: {2}}}",
AxisCount, ButtonCount, IsConnected);
}
/// <summary>
/// Serves as a hash function for a <see cref="OpenTK.Input.JoystickCapabilities"/> object.
/// </summary>
/// <returns>A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
/// hash table.</returns>
public override int GetHashCode()
{
return
AxisCount.GetHashCode() ^
ButtonCount.GetHashCode() ^
IsConnected.GetHashCode();
}
/// <summary>
/// Determines whether the specified <see cref="System.Object"/> is equal to the current <see cref="OpenTK.Input.JoystickCapabilities"/>.
/// </summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with the current <see cref="OpenTK.Input.JoystickCapabilities"/>.</param>
/// <returns><c>true</c> if the specified <see cref="System.Object"/> is equal to the current
/// <see cref="OpenTK.Input.JoystickCapabilities"/>; otherwise, <c>false</c>.</returns>
public override bool Equals(object obj)
{
return
obj is JoystickCapabilities &&
Equals((JoystickCapabilities)obj);
}
#endregion
#region Private Members
int DPadCount
{
get { return dpad_count; }
}
#endregion
#region IEquatable<JoystickCapabilities> Members
/// <summary>
/// Determines whether the specified <see cref="OpenTK.Input.JoystickCapabilities"/> is equal to the current <see cref="OpenTK.Input.JoystickCapabilities"/>.
/// </summary>
/// <param name="other">The <see cref="OpenTK.Input.JoystickCapabilities"/> to compare with the current <see cref="OpenTK.Input.JoystickCapabilities"/>.</param>
/// <returns><c>true</c> if the specified <see cref="OpenTK.Input.JoystickCapabilities"/> is equal to the current
/// <see cref="OpenTK.Input.JoystickCapabilities"/>; otherwise, <c>false</c>.</returns>
public bool Equals(JoystickCapabilities other)
{
return
AxisCount == other.AxisCount &&
ButtonCount == other.ButtonCount &&
IsConnected == other.IsConnected;
}
#endregion
}
}

View file

@ -155,13 +155,14 @@ namespace OpenTK.Input
#region JoystickDevice<TDetail> : JoystickDevice #region JoystickDevice<TDetail> : JoystickDevice
// Provides platform-specific information about the relevant JoystickDevice. // Provides platform-specific information about the relevant JoystickDevice.
internal sealed class JoystickDevice<TDetail> : JoystickDevice internal class JoystickDevice<TDetail> : JoystickDevice
where TDetail : new()
{ {
internal JoystickDevice(int id, int axes, int buttons) internal JoystickDevice(int id, int axes, int buttons)
: base(id, axes, buttons) : base(id, axes, buttons)
{ } { }
internal TDetail Details; internal TDetail Details = new TDetail();
} }
#endregion #endregion
@ -271,49 +272,6 @@ namespace OpenTK.Input
#endregion #endregion
#region JoystickButton
/// <summary>
/// Defines available JoystickDevice buttons.
/// </summary>
public enum JoystickButton
{
/// <summary>The first button of the JoystickDevice.</summary>
Button0 = 0,
/// <summary>The second button of the JoystickDevice.</summary>
Button1,
/// <summary>The third button of the JoystickDevice.</summary>
Button2,
/// <summary>The fourth button of the JoystickDevice.</summary>
Button3,
/// <summary>The fifth button of the JoystickDevice.</summary>
Button4,
/// <summary>The sixth button of the JoystickDevice.</summary>
Button5,
/// <summary>The seventh button of the JoystickDevice.</summary>
Button6,
/// <summary>The eighth button of the JoystickDevice.</summary>
Button7,
/// <summary>The ninth button of the JoystickDevice.</summary>
Button8,
/// <summary>The tenth button of the JoystickDevice.</summary>
Button9,
/// <summary>The eleventh button of the JoystickDevice.</summary>
Button10,
/// <summary>The twelfth button of the JoystickDevice.</summary>
Button11,
/// <summary>The thirteenth button of the JoystickDevice.</summary>
Button12,
/// <summary>The fourteenth button of the JoystickDevice.</summary>
Button13,
/// <summary>The fifteenth button of the JoystickDevice.</summary>
Button14,
/// <summary>The sixteenth button of the JoystickDevice.</summary>
Button15,
}
#endregion
#region JoystickButtonCollection #region JoystickButtonCollection
/// <summary> /// <summary>
@ -376,37 +334,6 @@ namespace OpenTK.Input
#endregion #endregion
#region JoystickAxis
/// <summary>
/// Defines available JoystickDevice axes.
/// </summary>
public enum JoystickAxis
{
/// <summary>The first axis of the JoystickDevice.</summary>
Axis0 = 0,
/// <summary>The second axis of the JoystickDevice.</summary>
Axis1,
/// <summary>The third axis of the JoystickDevice.</summary>
Axis2,
/// <summary>The fourth axis of the JoystickDevice.</summary>
Axis3,
/// <summary>The fifth axis of the JoystickDevice.</summary>
Axis4,
/// <summary>The sixth axis of the JoystickDevice.</summary>
Axis5,
/// <summary>The seventh axis of the JoystickDevice.</summary>
Axis6,
/// <summary>The eighth axis of the JoystickDevice.</summary>
Axis7,
/// <summary>The ninth axis of the JoystickDevice.</summary>
Axis8,
/// <summary>The tenth axis of the JoystickDevice.</summary>
Axis9,
}
#endregion
#region JoystickAxisCollection #region JoystickAxisCollection
/// <summary> /// <summary>

View file

@ -0,0 +1,260 @@
#region License
//
// JoystickState.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;
using System.Diagnostics;
using System.Text;
namespace OpenTK.Input
{
/// <summary>
/// Describes the current state of a <see cref="JoystickDevice"/>.
/// </summary>
public struct JoystickState : IEquatable<JoystickState>
{
// If we ever add more values to JoystickAxis or JoystickButton
// then we'll need to increase these limits.
internal const int MaxAxes = (int)JoystickAxis.Last;
internal const int MaxButtons = (int)JoystickButton.Last;
const float ConversionFactor = 1.0f / (short.MaxValue + 0.5f);
unsafe fixed short axes[MaxAxes];
int buttons;
int packet_number;
bool is_connected;
#region Public Members
/// <summary>
/// Gets a value between -1.0 and 1.0 representing the current offset of the specified <see cref="JoystickAxis"/>.
/// </summary>
/// <returns>
/// A value between -1.0 and 1.0 representing offset of the specified <see cref="JoystickAxis"/>.
/// If the specified axis does not exist, then the return value is 0.0. Use <see cref="Joystick.GetCapabilities"/>
/// to query the number of available axes.
/// </returns>
/// <param name="axis">The <see cref="JoystickAxis"/> to query.</param>
public float GetAxis(JoystickAxis axis)
{
return GetAxisRaw(axis) * ConversionFactor;
}
/// <summary>
/// Gets the current <see cref="ButtonState"/> of the specified <see cref="JoystickButton"/>.
/// </summary>
/// <returns><see cref="ButtonState.Pressed"/> if the specified button is pressed; otherwise, <see cref="ButtonState.Released"/>.</returns>
/// <param name="button">The <see cref="JoystickButton"/> to query.</param>
public ButtonState GetButton(JoystickButton button)
{
return (buttons & (1 << (int)button)) != 0 ? ButtonState.Pressed : ButtonState.Released;
}
/// <summary>
/// Gets a value indicating whether the specified <see cref="JoystickButton"/> is currently pressed.
/// </summary>
/// <returns>true if the specified button is pressed; otherwise, false.</returns>
/// <param name="button">The <see cref="JoystickButton"/> to query.</param>
public bool IsButtonDown(JoystickButton button)
{
return (buttons & (1 << (int)button)) != 0;
}
/// <summary>
/// Gets a value indicating whether the specified <see cref="JoystickButton"/> is currently released.
/// </summary>
/// <returns>true if the specified button is released; otherwise, false.</returns>
/// <param name="button">The <see cref="JoystickButton"/> to query.</param>
public bool IsButtonUp(JoystickButton button)
{
return (buttons & (1 << (int)button)) == 0;
}
/// <summary>
/// Gets a value indicating whether this instance is connected.
/// </summary>
/// <value><c>true</c> if this instance is connected; otherwise, <c>false</c>.</value>
public bool IsConnected
{
get { return is_connected; }
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.JoystickState"/>.
/// </summary>
/// <returns>A <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.JoystickState"/>.</returns>
public override string ToString()
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < MaxAxes; i++)
{
sb.Append(" ");
sb.Append(String.Format("{0:f4}", GetAxis(JoystickAxis.Axis0 + i)));
}
return String.Format(
"{{Axes:{0}; Buttons: {1}; IsConnected: {2}}}",
sb.ToString(),
Convert.ToString((int)buttons, 2).PadLeft(16, '0'),
IsConnected);
}
/// <summary>
/// Serves as a hash function for a <see cref="OpenTK.Input.JoystickState"/> object.
/// </summary>
/// <returns>A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
/// hash table.</returns>
public override int GetHashCode()
{
int hash = buttons.GetHashCode() ^ IsConnected.GetHashCode();
for (int i = 0; i < MaxAxes; i++)
{
hash ^= GetAxisUnsafe(i).GetHashCode();
}
return hash;
}
/// <summary>
/// Determines whether the specified <see cref="System.Object"/> is equal to the current <see cref="OpenTK.Input.JoystickState"/>.
/// </summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with the current <see cref="OpenTK.Input.JoystickState"/>.</param>
/// <returns><c>true</c> if the specified <see cref="System.Object"/> is equal to the current
/// <see cref="OpenTK.Input.JoystickState"/>; otherwise, <c>false</c>.</returns>
public override bool Equals(object obj)
{
return
obj is JoystickState &&
Equals((JoystickState)obj);
}
#endregion
#region Internal Members
internal int PacketNumber
{
get { return packet_number; }
}
internal short GetAxisRaw(JoystickAxis axis)
{
return GetAxisRaw((int)axis);
}
internal short GetAxisRaw(int axis)
{
short value = 0;
if (axis >= 0 && axis < MaxAxes)
{
value = GetAxisUnsafe(axis);
}
else
{
Debug.Print("[Joystick] Invalid axis {0}", axis);
}
return value;
}
internal void SetAxis(JoystickAxis axis, short value)
{
int index = (int)axis;
if (index < 0 || index >= MaxAxes)
throw new ArgumentOutOfRangeException("axis");
unsafe
{
fixed (short* paxes = axes)
{
*(paxes + index) = value;
}
}
}
internal void SetButton(JoystickButton button, bool value)
{
int index = 1 << (int)button;
if (value)
{
buttons |= index;
}
else
{
buttons &= ~index;
}
}
internal void SetIsConnected(bool value)
{
is_connected = value;
}
internal void SetPacketNumber(int number)
{
packet_number = number;
}
#endregion
#region Private Members
short GetAxisUnsafe(int index)
{
unsafe
{
fixed (short* paxis = axes)
{
return *(paxis + index);
}
}
}
#endregion
#region IEquatable<JoystickState> Members
/// <summary>
/// Determines whether the specified <see cref="OpenTK.Input.JoystickState"/> is equal to the current <see cref="OpenTK.Input.JoystickState"/>.
/// </summary>
/// <param name="other">The <see cref="OpenTK.Input.JoystickState"/> to compare with the current <see cref="OpenTK.Input.JoystickState"/>.</param>
/// <returns><c>true</c> if the specified <see cref="OpenTK.Input.JoystickState"/> is equal to the current
/// <see cref="OpenTK.Input.JoystickState"/>; otherwise, <c>false</c>.</returns>
public bool Equals(JoystickState other)
{
bool equals =
buttons == other.buttons &&
IsConnected == other.IsConnected;
for (int i = 0; equals && i < MaxAxes; i++)
{
equals &= GetAxisUnsafe(i) == other.GetAxisUnsafe(i);
}
return equals;
}
#endregion
}
}

View file

@ -201,6 +201,10 @@ namespace OpenTK.Input
{ {
if (keys[(int)key] != state || KeyRepeat) if (keys[(int)key] != state || KeyRepeat)
{ {
// limit scancode to 8bits, otherwise the assignment
// below will crash randomly
scancode &= 0xff;
keys[(int)key] = scancodes[scancode] = state; keys[(int)key] = scancodes[scancode] = state;
if (state && KeyDown != null) if (state && KeyDown != null)

View file

@ -291,6 +291,11 @@ namespace OpenTK.Input
IsConnected |= other.IsConnected; IsConnected |= other.IsConnected;
} }
internal void SetIsConnected(bool value)
{
IsConnected = value;
}
#endregion #endregion
#region Private Members #region Private Members

View file

@ -329,6 +329,11 @@ namespace OpenTK.Input
} }
} }
internal void SetIsConnected(bool value)
{
IsConnected = value;
}
#endregion #endregion
#region Private Members #region Private Members

View file

@ -288,6 +288,46 @@ namespace OpenTK
#endregion #endregion
#region Clamp
/// <summary>
/// Clamps a number between a minimum and a maximum.
/// </summary>
/// <param name="n">The number to clamp.</param>
/// <param name="min">The minimum allowed value.</param>
/// <param name="max">The maximum allowed value.</param>
/// <returns>min, if n is lower than min; max, if n is higher than max; n otherwise.</returns>
public static int Clamp(int n, int min, int max)
{
return Math.Max(Math.Min(n, max), min);
}
/// <summary>
/// Clamps a number between a minimum and a maximum.
/// </summary>
/// <param name="n">The number to clamp.</param>
/// <param name="min">The minimum allowed value.</param>
/// <param name="max">The maximum allowed value.</param>
/// <returns>min, if n is lower than min; max, if n is higher than max; n otherwise.</returns>
public static float Clamp(float n, float min, float max)
{
return Math.Max(Math.Min(n, max), min);
}
/// <summary>
/// Clamps a number between a minimum and a maximum.
/// </summary>
/// <param name="n">The number to clamp.</param>
/// <param name="min">The minimum allowed value.</param>
/// <param name="max">The maximum allowed value.</param>
/// <returns>min, if n is lower than min; max, if n is higher than max; n otherwise.</returns>
public static double Clamp(double n, double min, double max)
{
return Math.Max(Math.Min(n, max), min);
}
#endregion
#endregion #endregion
} }
} }

View file

@ -133,10 +133,19 @@
</Compile> </Compile>
<Compile Include="DisplayIndex.cs" /> <Compile Include="DisplayIndex.cs" />
<Compile Include="Graphics\GraphicsModeComparer.cs" /> <Compile Include="Graphics\GraphicsModeComparer.cs" />
<Compile Include="Input\GamePadThumbSticks.cs" />
<Compile Include="Input\GamePadTriggers.cs" />
<Compile Include="Input\GamePadType.cs" />
<Compile Include="Input\IGamePadDriver.cs" /> <Compile Include="Input\IGamePadDriver.cs" />
<Compile Include="Input\IInputDriver2.cs" /> <Compile Include="Input\IInputDriver2.cs" />
<Compile Include="Input\IJoystickDriver2.cs" />
<Compile Include="Input\IKeyboardDriver2.cs" /> <Compile Include="Input\IKeyboardDriver2.cs" />
<Compile Include="Input\IMouseDriver2.cs" /> <Compile Include="Input\IMouseDriver2.cs" />
<Compile Include="Input\Joystick.cs" />
<Compile Include="Input\JoystickAxis.cs" />
<Compile Include="Input\JoystickButton.cs" />
<Compile Include="Input\JoystickCapabilities.cs" />
<Compile Include="Input\JoystickState.cs" />
<Compile Include="InteropHelper.cs" /> <Compile Include="InteropHelper.cs" />
<Compile Include="Math\Matrix2.cs" /> <Compile Include="Math\Matrix2.cs" />
<Compile Include="Math\Matrix2d.cs" /> <Compile Include="Math\Matrix2d.cs" />
@ -154,7 +163,9 @@
<Compile Include="Math\Matrix4x3.cs" /> <Compile Include="Math\Matrix4x3.cs" />
<Compile Include="Math\Matrix4x3d.cs" /> <Compile Include="Math\Matrix4x3d.cs" />
<Compile Include="Platform\DisplayDeviceBase.cs" /> <Compile Include="Platform\DisplayDeviceBase.cs" />
<Compile Include="Platform\MappedGamePadDriver.cs" />
<Compile Include="Platform\Windows\WinInputBase.cs" /> <Compile Include="Platform\Windows\WinInputBase.cs" />
<Compile Include="Platform\Windows\XInputJoystick.cs" />
<Compile Include="ToolkitOptions.cs" /> <Compile Include="ToolkitOptions.cs" />
<Compile Include="WindowBorder.cs"> <Compile Include="WindowBorder.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
@ -738,8 +749,7 @@
</EmbeddedResource> </EmbeddedResource>
<Compile Include="Platform\MacOS\HIDInput.cs" /> <Compile Include="Platform\MacOS\HIDInput.cs" />
<Compile Include="IntPtrEqualityComparer.cs" /> <Compile Include="IntPtrEqualityComparer.cs" />
<Compile Include="Input\GamePadButton.cs" /> <Compile Include="Input\GamePadAxes.cs" />
<Compile Include="Input\GamePadAxis.cs" />
<Compile Include="Platform\SDL2\Sdl2DisplayDeviceDriver.cs" /> <Compile Include="Platform\SDL2\Sdl2DisplayDeviceDriver.cs" />
<Compile Include="Platform\SDL2\Sdl2Factory.cs" /> <Compile Include="Platform\SDL2\Sdl2Factory.cs" />
<Compile Include="Platform\SDL2\Sdl2GraphicsContext.cs" /> <Compile Include="Platform\SDL2\Sdl2GraphicsContext.cs" />
@ -777,6 +787,17 @@
<Compile Include="SlotAttribute.cs" /> <Compile Include="SlotAttribute.cs" />
<Compile Include="RewrittenAttribute.cs" /> <Compile Include="RewrittenAttribute.cs" />
<Compile Include="Graphics\OpenGL\GLObsolete.cs" /> <Compile Include="Graphics\OpenGL\GLObsolete.cs" />
<Compile Include="Platform\MacOS\NS.cs" />
<Compile Include="Input\GamePadCapabilities.cs" />
<Compile Include="Input\GamePadDPad.cs" />
<Compile Include="Input\GamePadButtons.cs" />
<Compile Include="Input\Buttons.cs" />
<Compile Include="Input\GamePadConfigurationDatabase.cs" />
<Compile Include="Input\GamePadConfiguration.cs" />
<Compile Include="Input\GamePadConfigurationTarget.cs" />
<Compile Include="Input\ConfigurationType.cs" />
<Compile Include="Input\GamePadConfigurationSource.cs" />
<Compile Include="Input\GamePadConfigurationItem.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>

View file

@ -22,7 +22,8 @@ namespace OpenTK.Platform.Dummy
/// </summary> /// </summary>
internal sealed class DummyGLContext : DesktopGraphicsContext internal sealed class DummyGLContext : DesktopGraphicsContext
{ {
// This mode is not real. To receive a real mode we'd have to create a temporary context, which is not desirable! readonly GraphicsContext.GetAddressDelegate Loader;
bool vsync; bool vsync;
int swap_interval; int swap_interval;
static int handle_count; static int handle_count;
@ -31,13 +32,20 @@ namespace OpenTK.Platform.Dummy
#region --- Constructors --- #region --- Constructors ---
public DummyGLContext() public DummyGLContext()
: this(new ContextHandle(new IntPtr(++handle_count)))
{ {
Handle = new ContextHandle(
new IntPtr(Interlocked.Increment(
ref handle_count)));
} }
public DummyGLContext(ContextHandle handle) public DummyGLContext(ContextHandle handle, GraphicsContext.GetAddressDelegate loader)
: this()
{
if (handle != ContextHandle.Zero)
{ {
Handle = handle; Handle = handle;
}
Loader = loader;
Mode = new GraphicsMode(new IntPtr(2), 32, 16, 0, 0, 0, 2, false); Mode = new GraphicsMode(new IntPtr(2), 32, 16, 0, 0, 0, 2, false);
} }
@ -45,15 +53,6 @@ namespace OpenTK.Platform.Dummy
#region --- IGraphicsContext Members --- #region --- IGraphicsContext Members ---
public void CreateContext(bool direct, IGraphicsContext source)
{
if (Handle == ContextHandle.Zero)
{
++handle_count;
Handle = new ContextHandle((IntPtr)handle_count);
}
}
public override void SwapBuffers() { } public override void SwapBuffers() { }
public override void MakeCurrent(IWindowInfo info) public override void MakeCurrent(IWindowInfo info)
@ -81,9 +80,15 @@ namespace OpenTK.Platform.Dummy
get { return current_thread != null && current_thread == Thread.CurrentThread; } get { return current_thread != null && current_thread == Thread.CurrentThread; }
} }
public override IntPtr GetAddress(string function) { return IntPtr.Zero; } public override IntPtr GetAddress(string function)
{
return Loader(function);
}
public override IntPtr GetAddress(IntPtr function) { return IntPtr.Zero; } public override IntPtr GetAddress(IntPtr function)
{
return IntPtr.Zero;
}
public override int SwapInterval public override int SwapInterval
{ {
@ -101,7 +106,14 @@ namespace OpenTK.Platform.Dummy
{ } { }
public override void LoadAll() public override void LoadAll()
{ } {
new OpenTK.Graphics.OpenGL.GL().LoadEntryPoints();
new OpenTK.Graphics.OpenGL4.GL().LoadEntryPoints();
new OpenTK.Graphics.ES10.GL().LoadEntryPoints();
new OpenTK.Graphics.ES11.GL().LoadEntryPoints();
new OpenTK.Graphics.ES20.GL().LoadEntryPoints();
new OpenTK.Graphics.ES30.GL().LoadEntryPoints();
}
#endregion #endregion

View file

@ -149,6 +149,11 @@ namespace OpenTK.Platform
return default_implementation.CreateGamePadDriver(); return default_implementation.CreateGamePadDriver();
} }
public Input.IJoystickDriver2 CreateJoystickDriver()
{
return default_implementation.CreateJoystickDriver();
}
class UnsupportedPlatform : IPlatformFactory class UnsupportedPlatform : IPlatformFactory
{ {
#region Fields #region Fields
@ -210,6 +215,11 @@ namespace OpenTK.Platform
throw new PlatformNotSupportedException(error_string); throw new PlatformNotSupportedException(error_string);
} }
public Input.IJoystickDriver2 CreateJoystickDriver()
{
throw new PlatformNotSupportedException(error_string);
}
#endregion #endregion
#region IDisposable Members #region IDisposable Members

View file

@ -52,5 +52,7 @@ namespace OpenTK.Platform
OpenTK.Input.IMouseDriver2 CreateMouseDriver(); OpenTK.Input.IMouseDriver2 CreateMouseDriver();
OpenTK.Input.IGamePadDriver CreateGamePadDriver(); OpenTK.Input.IGamePadDriver CreateGamePadDriver();
Input.IJoystickDriver2 CreateJoystickDriver();
} }
} }

View file

@ -464,62 +464,14 @@ namespace OpenTK.Platform.MacOS
#region IGraphicsContextInternal Members #region IGraphicsContextInternal Members
private const string Library = "libdl.dylib";
[DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")]
private static extern bool NSIsSymbolNameDefined(string s);
[DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")]
private static extern bool NSIsSymbolNameDefined(IntPtr s);
[DllImport(Library, EntryPoint = "NSLookupAndBindSymbol")]
private static extern IntPtr NSLookupAndBindSymbol(string s);
[DllImport(Library, EntryPoint = "NSLookupAndBindSymbol")]
private static extern IntPtr NSLookupAndBindSymbol(IntPtr s);
[DllImport(Library, EntryPoint = "NSAddressOfSymbol")]
private static extern IntPtr NSAddressOfSymbol(IntPtr symbol);
public override IntPtr GetAddress(string function) public override IntPtr GetAddress(string function)
{ {
// Instead of allocating and combining strings in managed memory return NS.GetAddress(function);
// we do that directly in unmanaged memory. This way, we avoid
// 2 string allocations every time this function is called.
// must add a '_' prefix and null-terminate the function name,
// hence we allocate +2 bytes
IntPtr ptr = Marshal.AllocHGlobal(function.Length + 2);
try
{
Marshal.WriteByte(ptr, (byte)'_');
for (int i = 0; i < function.Length; i++)
{
Marshal.WriteByte(ptr, i + 1, (byte)function[i]);
}
Marshal.WriteByte(ptr, function.Length + 1, 0); // null-terminate
IntPtr symbol = IntPtr.Zero;
if (NSIsSymbolNameDefined(ptr))
{
symbol = NSLookupAndBindSymbol(ptr);
if (symbol != IntPtr.Zero)
symbol = NSAddressOfSymbol(symbol);
}
return symbol;
}
finally
{
Marshal.FreeHGlobal(ptr);
}
} }
public override IntPtr GetAddress(IntPtr function) public override IntPtr GetAddress(IntPtr function)
{ {
if (!NSIsSymbolNameDefined(function)) return NS.GetAddress(function);
return IntPtr.Zero;
IntPtr symbol = NSLookupAndBindSymbol(function);
if (symbol != IntPtr.Zero)
symbol = NSAddressOfSymbol(symbol);
return symbol;
} }
#endregion #endregion

View file

@ -64,6 +64,11 @@ namespace OpenTK.Platform.MacOS.Carbon
short bottom; short bottom;
short right; short right;
internal Rect(int left, int top, int width, int height)
: this((short)left, (short)top, (short)width, (short)height)
{
}
internal Rect(short _left, short _top, short _width, short _height) internal Rect(short _left, short _top, short _width, short _height)
{ {
top = _top; top = _top;
@ -236,6 +241,7 @@ namespace OpenTK.Platform.MacOS.Carbon
WindowClickProxyIconRgn = 38, WindowClickProxyIconRgn = 38,
WindowClose = 72, WindowClose = 72,
WindowClosed = 73, WindowClosed = 73,
WindowPaint = 1013,
} }
internal enum MouseEventKind : int internal enum MouseEventKind : int
{ {
@ -383,7 +389,35 @@ namespace OpenTK.Platform.MacOS.Carbon
SideTitlebar = (1u << 5), /* window wants a titlebar on the side (floating window class only)*/ SideTitlebar = (1u << 5), /* window wants a titlebar on the side (floating window class only)*/
NoUpdates = (1u << 16), /* this window receives no update events*/ NoUpdates = (1u << 16), /* this window receives no update events*/
NoActivates = (1u << 17), /* this window receives no activate events*/ NoActivates = (1u << 17), /* this window receives no activate events*/
NoBuffering = (1u << 20), /* this window is not buffered (Mac OS X only)*/
/// <summary>
/// This window uses composited drawing. This means that the entire
/// window is comprised of HIViews, and can be treated thusly. This
/// attribute must be specified at window creation, and cannot be
/// changed later with ChangeWindows. In 64-bit mode, all windows must
/// be compositing, and you must always specify this attribute when
/// creating a window from code or designing a window in Interface
/// Builder. Available on Mac OS X 10.2 and later.
/// </summary>
Compositing = (1u << 19),
/// <summary>
/// This window's context should be scaled to match the display scale
/// factor. This attribute can only be used when
/// kHIWindowBitCompositing is also enabled. When this attribute is
/// enabled, you may not draw with QuickDraw in the window. If this
/// attribute is enabled and if the scale factor is something other
/// than 1.0, the window's scale mode will be
/// kHIWindowScaleModeFrameworkScaled. You may only specify this
/// attribute at window creation time. Available for all windows in
/// Mac OS X 10.4 and later.
/// </summary>
FrameworkScaled = (1u << 20),
/// <summary>
/// This window has the standard Carbon window event handler
/// installed. Available for all windows.
/// </summary>
StandardHandler = (1u << 25), StandardHandler = (1u << 25),
InWindowMenu = (1u << 27), InWindowMenu = (1u << 27),
LiveResize = (1u << 28), LiveResize = (1u << 28),
@ -530,6 +564,9 @@ namespace OpenTK.Platform.MacOS.Carbon
return retval; return retval;
} }
[DllImport(carbon)]
internal static extern OSStatus SetWindowBounds(IntPtr Windows, WindowRegionCode WindowRegionCode, ref Rect globalBounds);
//[DllImport(carbon)] //[DllImport(carbon)]
//internal static extern void MoveWindow(IntPtr window, short hGlobal, short vGlobal, bool front); //internal static extern void MoveWindow(IntPtr window, short hGlobal, short vGlobal, bool front);
@ -1006,6 +1043,12 @@ namespace OpenTK.Platform.MacOS.Carbon
return retval; return retval;
} }
//[DllImport(carbon)]
//static extern OSStatus HIWindowCreate(WindowClass class, int[] attributes,
// ref WindowDefSpec defSpec, HICoordinateSpace space, ref HIRect bounds,
// out IntPtr window);
#region --- SetWindowTitle --- #region --- SetWindowTitle ---
[DllImport(carbon)] [DllImport(carbon)]

View file

@ -32,7 +32,10 @@ using System.Text;
namespace OpenTK.Platform.MacOS.Carbon namespace OpenTK.Platform.MacOS.Carbon
{ {
using CFIndex = System.IntPtr;
using CFRunLoop = System.IntPtr; using CFRunLoop = System.IntPtr;
using CFStringRef = System.IntPtr;
using CFTypeRef = System.IntPtr;
struct CFArray struct CFArray
{ {
@ -59,6 +62,7 @@ namespace OpenTK.Platform.MacOS.Carbon
} }
} }
} }
struct CFDictionary struct CFDictionary
{ {
public CFDictionary(IntPtr reference) public CFDictionary(IntPtr reference)
@ -76,20 +80,18 @@ namespace OpenTK.Platform.MacOS.Carbon
return CF.CFDictionaryGetCount(dictionaryRef); return CF.CFDictionaryGetCount(dictionaryRef);
} }
} }
public double GetNumberValue(string key) public double GetNumberValue(string key)
{
unsafe
{ {
double retval; double retval;
IntPtr cfnum = CF.CFDictionaryGetValue(dictionaryRef, IntPtr cfnum = CF.CFDictionaryGetValue(dictionaryRef,
CF.CFSTR(key)); CF.CFSTR(key));
CF.CFNumberGetValue(cfnum, CF.CFNumberType.kCFNumberDoubleType, &retval); CF.CFNumberGetValue(cfnum, CF.CFNumberType.kCFNumberDoubleType, out retval);
return retval; return retval;
} }
} }
}
class CF class CF
{ {
const string appServices = "/System/Library/Frameworks/ApplicationServices.framework/Versions/Current/ApplicationServices"; const string appServices = "/System/Library/Frameworks/ApplicationServices.framework/Versions/Current/ApplicationServices";
@ -106,6 +108,9 @@ namespace OpenTK.Platform.MacOS.Carbon
[DllImport(appServices)] [DllImport(appServices)]
internal static extern IntPtr CFDictionaryGetValue(IntPtr theDictionary, IntPtr theKey); internal static extern IntPtr CFDictionaryGetValue(IntPtr theDictionary, IntPtr theKey);
[DllImport(appServices)]
internal static extern void CFRelease(CFTypeRef cf);
// this mirrors the definition in CFString.h. // this mirrors the definition in CFString.h.
// I don't know why, but __CFStringMakeConstantString is marked as "private and should not be used directly" // I don't know why, but __CFStringMakeConstantString is marked as "private and should not be used directly"
// even though the CFSTR macro just calls it. // even though the CFSTR macro just calls it.
@ -117,9 +122,38 @@ namespace OpenTK.Platform.MacOS.Carbon
} }
[DllImport(appServices)] [DllImport(appServices)]
internal unsafe static extern bool CFNumberGetValue (IntPtr number, CFNumberType theType, int* valuePtr); internal static extern Boolean CFStringGetCString(
CFStringRef theString,
byte[] buffer,
CFIndex bufferSize,
CFStringEncoding encoding
);
internal static string CFStringGetCString(IntPtr cfstr)
{
CFIndex length = CFStringGetLength(cfstr);
if (length != IntPtr.Zero)
{
byte[] utf8_chars = new byte[length.ToInt32() + 1];
if (CFStringGetCString(cfstr, utf8_chars, new IntPtr(utf8_chars.Length), CFStringEncoding.UTF8))
{
return Encoding.UTF8.GetString(utf8_chars);
}
}
return String.Empty;
}
[DllImport(appServices)] [DllImport(appServices)]
internal unsafe static extern bool CFNumberGetValue(IntPtr number, CFNumberType theType, double* valuePtr); internal static extern CFIndex CFStringGetLength(
CFStringRef theString
);
[DllImport(appServices)]
internal static extern bool CFNumberGetValue (IntPtr number, CFNumberType theType, out int valuePtr);
[DllImport(appServices)]
internal static extern bool CFNumberGetValue (IntPtr number, CFNumberType theType, out long valuePtr);
[DllImport(appServices)]
internal static extern bool CFNumberGetValue(IntPtr number, CFNumberType theType, out double valuePtr);
internal enum CFNumberType internal enum CFNumberType
{ {
@ -150,6 +184,25 @@ namespace OpenTK.Platform.MacOS.Carbon
HandledSource = 4 HandledSource = 4
} }
public enum CFStringEncoding
{
MacRoman = 0,
WindowsLatin1 = 0x0500,
ISOLatin1 = 0x0201,
NextStepLatin = 0x0B01,
ASCII = 0x0600,
Unicode = 0x0100,
UTF8 = 0x08000100,
NonLossyASCII = 0x0BFF,
UTF16 = 0x0100,
UTF16BE = 0x10000100,
UTF16LE = 0x14000100,
UTF32 = 0x0c000100,
UTF32BE = 0x18000100,
UTF32LE = 0x1c000100
}
public static readonly IntPtr RunLoopModeDefault = CF.CFSTR("kCFRunLoopDefaultMode"); public static readonly IntPtr RunLoopModeDefault = CF.CFSTR("kCFRunLoopDefaultMode");
[DllImport(appServices)] [DllImport(appServices)]

View file

@ -74,6 +74,8 @@ namespace OpenTK.Platform.MacOS
new Dictionary<IntPtr, WeakReference>(new IntPtrEqualityComparer()); new Dictionary<IntPtr, WeakReference>(new IntPtrEqualityComparer());
KeyPressEventArgs mKeyPressArgs = new KeyPressEventArgs((char)0); KeyPressEventArgs mKeyPressArgs = new KeyPressEventArgs((char)0);
OpenTK.Input.KeyboardKeyEventArgs mKeyDownArgs = new OpenTK.Input.KeyboardKeyEventArgs();
OpenTK.Input.KeyboardKeyEventArgs mKeyUpArgs = new OpenTK.Input.KeyboardKeyEventArgs();
bool mMouseIn = false; bool mMouseIn = false;
bool mIsActive = false; bool mIsActive = false;
@ -106,18 +108,6 @@ namespace OpenTK.Platform.MacOS
Application.Initialize(); Application.Initialize();
} }
CarbonGLNative() : this(WindowClass.Document,
WindowAttributes.StandardDocument | WindowAttributes.StandardHandler |
WindowAttributes.InWindowMenu | WindowAttributes.LiveResize)
{
}
CarbonGLNative(WindowClass @class, WindowAttributes attrib)
{
mWindowClass = @class;
mWindowAttrib = attrib;
}
public CarbonGLNative(int x, int y, int width, int height, string title, public CarbonGLNative(int x, int y, int width, int height, string title,
GraphicsMode mode, GameWindowFlags options, DisplayDevice device) GraphicsMode mode, GameWindowFlags options, DisplayDevice device)
{ {
@ -382,7 +372,14 @@ namespace OpenTK.Platform.MacOS
case KeyboardEventKind.RawKeyDown: case KeyboardEventKind.RawKeyDown:
Keymap.TryGetValue(code, out key); Keymap.TryGetValue(code, out key);
// Legacy keyboard API
InputDriver.Keyboard[0].SetKey(key, (uint)code, true); InputDriver.Keyboard[0].SetKey(key, (uint)code, true);
// Raise KeyDown for new keyboard API
mKeyDownArgs.Key = key;
KeyDown(this, mKeyDownArgs);
// Raise KeyPress for new keyboard API
if (!Char.IsControl(mKeyPressArgs.KeyChar)) if (!Char.IsControl(mKeyPressArgs.KeyChar))
{ {
OnKeyPress(mKeyPressArgs); OnKeyPress(mKeyPressArgs);
@ -391,7 +388,12 @@ namespace OpenTK.Platform.MacOS
case KeyboardEventKind.RawKeyUp: case KeyboardEventKind.RawKeyUp:
Keymap.TryGetValue(code, out key); Keymap.TryGetValue(code, out key);
// Legacy keyboard API
InputDriver.Keyboard[0].SetKey(key, (uint)code, false); InputDriver.Keyboard[0].SetKey(key, (uint)code, false);
// Raise KeyUp for new keyboard API
mKeyUpArgs.Key = key;
KeyUp(this, mKeyUpArgs);
return OSStatus.NoError; return OSStatus.NoError;
case KeyboardEventKind.RawKeyModifiersChanged: case KeyboardEventKind.RawKeyModifiersChanged:
@ -640,41 +642,40 @@ namespace OpenTK.Platform.MacOS
} }
Rect GetRegion() Rect GetClientSize()
{ {
Rect retval = API.GetWindowBounds(window.Handle, WindowRegionCode.ContentRegion); Rect retval = API.GetWindowBounds(window.Handle, WindowRegionCode.ContentRegion);
return retval; return retval;
} }
void SetLocation(short x, short y)
{
if (windowState == WindowState.Fullscreen)
return;
API.MoveWindow(window.Handle, x, y, false);
}
void SetSize(short width, short height)
{
if (WindowState == WindowState.Fullscreen)
return;
// The bounds of the window should be the size specified, but
// API.SizeWindow sets the content region size. So
// we reduce the size to get the correct bounds.
width -= (short)(bounds.Width - clientRectangle.Width);
height -= (short)(bounds.Height - clientRectangle.Height);
API.SizeWindow(window.Handle, width, height, true);
}
void SetClientSize(short width, short height) void SetClientSize(short width, short height)
{ {
if (WindowState == WindowState.Fullscreen) if (WindowState == WindowState.Fullscreen)
return; return;
API.SizeWindow(window.Handle, width, height, true); Rect new_bounds = new Rect(Bounds.X, Bounds.Y, width, height);
API.SetWindowBounds(window.Handle, WindowRegionCode.ContentRegion, ref new_bounds);
LoadSize();
}
void SetLocation(short x, short y)
{
if (windowState == WindowState.Fullscreen)
return;
Rect new_bounds = new Rect(x, y, Bounds.Width, Bounds.Height);
API.SetWindowBounds(window.Handle, WindowRegionCode.StructureRegion, ref new_bounds);
LoadSize();
}
void SetSize(short width, short height)
{
if (WindowState == WindowState.Fullscreen)
return;
Rect new_bounds = new Rect(Bounds.X, Bounds.Y, width, height);
API.SetWindowBounds(window.Handle, WindowRegionCode.StructureRegion, ref new_bounds);
LoadSize();
} }
private void LoadSize() private void LoadSize()
@ -685,7 +686,7 @@ namespace OpenTK.Platform.MacOS
Rect r = API.GetWindowBounds(window.Handle, WindowRegionCode.StructureRegion); Rect r = API.GetWindowBounds(window.Handle, WindowRegionCode.StructureRegion);
bounds = new Rectangle(r.X, r.Y, r.Width, r.Height); bounds = new Rectangle(r.X, r.Y, r.Width, r.Height);
r = API.GetWindowBounds(window.Handle, WindowRegionCode.GlobalPortRegion); r = API.GetWindowBounds(window.Handle, WindowRegionCode.ContentRegion);
clientRectangle = new Rectangle(0, 0, r.Width, r.Height); clientRectangle = new Rectangle(0, 0, r.Width, r.Height);
} }
@ -871,13 +872,13 @@ namespace OpenTK.Platform.MacOS
public int X public int X
{ {
get { return ClientRectangle.X; } get { return Bounds.X; }
set { Location = new Point(value, Y); } set { Location = new Point(value, Y); }
} }
public int Y public int Y
{ {
get { return ClientRectangle.Y; } get { return Bounds.Y; }
set { Location = new Point(X, value); } set { Location = new Point(X, value); }
} }

View file

@ -113,5 +113,10 @@ namespace OpenTK.Platform.MacOS
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
public IJoystickDriver2 JoystickDriver
{
get { throw new NotImplementedException(); }
}
} }
} }

View file

@ -35,6 +35,7 @@ namespace OpenTK.Platform.MacOS
{ {
using Carbon; using Carbon;
using CFAllocatorRef = System.IntPtr; using CFAllocatorRef = System.IntPtr;
using CFArrayRef = System.IntPtr;
using CFDictionaryRef = System.IntPtr; using CFDictionaryRef = System.IntPtr;
using CFIndex = System.IntPtr; using CFIndex = System.IntPtr;
using CFRunLoop = System.IntPtr; using CFRunLoop = System.IntPtr;
@ -50,25 +51,53 @@ namespace OpenTK.Platform.MacOS
// Requires Mac OS X 10.5 or higher. // Requires Mac OS X 10.5 or higher.
// Todo: create a driver for older installations. Maybe use CGGetLastMouseDelta for that? // Todo: create a driver for older installations. Maybe use CGGetLastMouseDelta for that?
class HIDInput : IInputDriver2, IMouseDriver2, IKeyboardDriver2/*, IGamePadDriver*/ class HIDInput : IInputDriver2, IMouseDriver2, IKeyboardDriver2, IJoystickDriver2
{ {
#region Fields #region Fields
class MouseData
{
public MouseState State;
}
class KeyboardData
{
public KeyboardState State;
}
class JoystickData
{
public string Name;
public Guid Guid;
public JoystickState State;
public JoystickCapabilities Capabilities;
readonly public Dictionary<int, JoystickButton> ElementUsageToButton =
new Dictionary<int, JoystickButton>();
}
readonly IOHIDManagerRef hidmanager; readonly IOHIDManagerRef hidmanager;
readonly Dictionary<IntPtr, MouseState> MouseDevices = readonly Dictionary<IntPtr, MouseData> MouseDevices =
new Dictionary<IntPtr, MouseState>(new IntPtrEqualityComparer()); new Dictionary<IntPtr, MouseData>(new IntPtrEqualityComparer());
readonly Dictionary<int, IntPtr> MouseIndexToDevice = readonly Dictionary<int, IntPtr> MouseIndexToDevice =
new Dictionary<int, IntPtr>(); new Dictionary<int, IntPtr>();
readonly Dictionary<IntPtr, KeyboardState> KeyboardDevices =
new Dictionary<IntPtr, KeyboardState>(new IntPtrEqualityComparer()); readonly Dictionary<IntPtr, KeyboardData> KeyboardDevices =
new Dictionary<IntPtr, KeyboardData>(new IntPtrEqualityComparer());
readonly Dictionary<int, IntPtr> KeyboardIndexToDevice = readonly Dictionary<int, IntPtr> KeyboardIndexToDevice =
new Dictionary<int, IntPtr>(); new Dictionary<int, IntPtr>();
readonly Dictionary<IntPtr, JoystickData> JoystickDevices =
new Dictionary<IntPtr, JoystickData>(new IntPtrEqualityComparer());
readonly Dictionary<int, IntPtr> JoystickIndexToDevice =
new Dictionary<int, IntPtr>();
readonly CFRunLoop RunLoop = CF.CFRunLoopGetMain(); readonly CFRunLoop RunLoop = CF.CFRunLoopGetMain();
readonly CFString InputLoopMode = CF.RunLoopModeDefault; readonly CFString InputLoopMode = CF.RunLoopModeDefault;
readonly CFDictionary DeviceTypes = new CFDictionary(); readonly CFDictionary DeviceTypes = new CFDictionary();
readonly MappedGamePadDriver mapped_gamepad = new MappedGamePadDriver();
NativeMethods.IOHIDDeviceCallback HandleDeviceAdded; NativeMethods.IOHIDDeviceCallback HandleDeviceAdded;
NativeMethods.IOHIDDeviceCallback HandleDeviceRemoved; NativeMethods.IOHIDDeviceCallback HandleDeviceRemoved;
NativeMethods.IOHIDValueCallback HandleDeviceValueReceived; NativeMethods.IOHIDValueCallback HandleDeviceValueReceived;
@ -119,48 +148,44 @@ namespace OpenTK.Platform.MacOS
void DeviceAdded(IntPtr context, IOReturn res, IntPtr sender, IOHIDDeviceRef device) void DeviceAdded(IntPtr context, IOReturn res, IntPtr sender, IOHIDDeviceRef device)
{ {
try
{
bool recognized = false;
if (NativeMethods.IOHIDDeviceOpen(device, IOOptionBits.Zero) == IOReturn.Zero) if (NativeMethods.IOHIDDeviceOpen(device, IOOptionBits.Zero) == IOReturn.Zero)
{ {
if (NativeMethods.IOHIDDeviceConformsTo(device, if (NativeMethods.IOHIDDeviceConformsTo(device,
HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse)) HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse))
{ {
if (!MouseDevices.ContainsKey(device)) AddMouse(sender, device);
{ recognized = true;
Debug.Print("Mouse device {0:x} discovered, sender is {1:x}", device, sender);
MouseState state = new MouseState();
state.IsConnected = true;
MouseIndexToDevice.Add(MouseDevices.Count, device);
MouseDevices.Add(device, state);
}
else
{
Debug.Print("Mouse device {0:x} reconnected, sender is {1:x}", device, sender);
MouseState state = MouseDevices[device];
state.IsConnected = true;
MouseDevices[device] = state;
}
} }
if (NativeMethods.IOHIDDeviceConformsTo(device, if (NativeMethods.IOHIDDeviceConformsTo(device,
HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard)) HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard))
{ {
if (!KeyboardDevices.ContainsKey(device)) AddKeyboard(sender, device);
{ recognized = true;
Debug.Print("Keyboard device {0:x} discovered, sender is {1:x}", device, sender);
KeyboardState state = new KeyboardState();
state.IsConnected = true;
KeyboardIndexToDevice.Add(KeyboardDevices.Count, device);
KeyboardDevices.Add(device, state);
}
else
{
Debug.Print("Keyboard device {0:x} reconnected, sender is {1:x}", device, sender);
KeyboardState state = KeyboardDevices[device];
state.IsConnected = true;
KeyboardDevices[device] = state;
}
} }
bool is_joystick = false;
is_joystick |= NativeMethods.IOHIDDeviceConformsTo(device,
HIDPage.GenericDesktop, (int)HIDUsageGD.Joystick);
is_joystick |= NativeMethods.IOHIDDeviceConformsTo(device,
HIDPage.GenericDesktop, (int)HIDUsageGD.GamePad);
is_joystick |= NativeMethods.IOHIDDeviceConformsTo(device,
HIDPage.GenericDesktop, (int)HIDUsageGD.MultiAxisController);
is_joystick |= NativeMethods.IOHIDDeviceConformsTo(device,
HIDPage.GenericDesktop, (int)HIDUsageGD.Wheel);
// Todo: any other interesting devices under HIDPage.Simulation + HIDUsageSim?
if (is_joystick)
{
AddJoystick(sender, device);
recognized = true;
}
if (recognized)
{
// The device is not normally available in the InputValueCallback (HandleDeviceValueReceived), so we include // The device is not normally available in the InputValueCallback (HandleDeviceValueReceived), so we include
// the device identifier as the context variable, so we can identify it and figure out the device later. // the device identifier as the context variable, so we can identify it and figure out the device later.
// Thanks to Jase: http://www.opentk.com/node/2800 // Thanks to Jase: http://www.opentk.com/node/2800
@ -170,36 +195,52 @@ namespace OpenTK.Platform.MacOS
NativeMethods.IOHIDDeviceScheduleWithRunLoop(device, RunLoop, InputLoopMode); NativeMethods.IOHIDDeviceScheduleWithRunLoop(device, RunLoop, InputLoopMode);
} }
} }
}
catch (Exception e)
{
Debug.Print("[Mac] Exception in managed callback: {0}", e);
}
}
void DeviceRemoved(IntPtr context, IOReturn res, IntPtr sender, IOHIDDeviceRef device) void DeviceRemoved(IntPtr context, IOReturn res, IntPtr sender, IOHIDDeviceRef device)
{ {
if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse) && try
MouseDevices.ContainsKey(device))
{ {
Debug.Print("Mouse device {0:x} disconnected, sender is {1:x}", device, sender); bool recognized = false;
// Keep the device in case it comes back later on if (MouseDevices.ContainsKey(device))
MouseState state = MouseDevices[device]; {
state.IsConnected = false; RemoveMouse(sender, device);
MouseDevices[device] = state; recognized = true;
} }
if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard) && if (KeyboardDevices.ContainsKey(device))
KeyboardDevices.ContainsKey(device))
{ {
Debug.Print("Keyboard device {0:x} disconnected, sender is {1:x}", device, sender); RemoveKeyboard(sender, device);
recognized = true;
// Keep the device in case it comes back later on
KeyboardState state = KeyboardDevices[device];
state.IsConnected = false;
KeyboardDevices[device] = state;
} }
if (JoystickDevices.ContainsKey(device))
{
RemoveJoystick(sender, device);
recognized = true;
}
if (recognized)
{
NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, IntPtr.Zero, IntPtr.Zero); NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, IntPtr.Zero, IntPtr.Zero);
NativeMethods.IOHIDDeviceUnscheduleWithRunLoop(device, RunLoop, InputLoopMode); NativeMethods.IOHIDDeviceUnscheduleFromRunLoop(device, RunLoop, InputLoopMode);
}
}
catch (Exception e)
{
Debug.Print("[Mac] Exception in managed callback: {0}", e);
}
} }
void DeviceValueReceived(IntPtr context, IOReturn res, IntPtr sender, IOHIDValueRef val) void DeviceValueReceived(IntPtr context, IOReturn res, IntPtr sender, IOHIDValueRef val)
{
try
{ {
if (disposed) if (disposed)
{ {
@ -208,21 +249,57 @@ namespace OpenTK.Platform.MacOS
return; return;
} }
MouseState mouse; MouseData mouse;
KeyboardState keyboard; KeyboardData keyboard;
JoystickData joystick;
if (MouseDevices.TryGetValue(context, out mouse)) if (MouseDevices.TryGetValue(context, out mouse))
{ {
MouseDevices[context] = UpdateMouse(mouse, val); UpdateMouse(mouse, val);
} }
else if (KeyboardDevices.TryGetValue(context, out keyboard)) else if (KeyboardDevices.TryGetValue(context, out keyboard))
{ {
KeyboardDevices[context] = UpdateKeyboard(keyboard, val); UpdateKeyboard(keyboard, val);
}else{ }
else if (JoystickDevices.TryGetValue(context, out joystick))
{
UpdateJoystick(joystick, val);
}
else
{
//Debug.Print ("Device {0:x} not found in list of keyboards or mice", sender); //Debug.Print ("Device {0:x} not found in list of keyboards or mice", sender);
} }
} }
catch (Exception e)
{
Debug.Print("[Mac] Exception in managed callback: {0}", e);
}
}
static MouseState UpdateMouse(MouseState state, IOHIDValueRef val) #region Mouse
void AddMouse(CFAllocatorRef sender, CFAllocatorRef device)
{
if (!MouseDevices.ContainsKey(device))
{
Debug.Print("Mouse device {0:x} discovered, sender is {1:x}", device, sender);
MouseIndexToDevice.Add(MouseDevices.Count, device);
MouseDevices.Add(device, new MouseData());
}
else
{
Debug.Print("Mouse device {0:x} reconnected, sender is {1:x}", device, sender);
}
MouseDevices[device].State.SetIsConnected(true);
}
void RemoveMouse(CFAllocatorRef sender, CFAllocatorRef device)
{
Debug.Print("Mouse device {0:x} disconnected, sender is {1:x}", device, sender);
// Keep the device in case it comes back later on
MouseDevices[device].State.SetIsConnected(false);
}
static void UpdateMouse(MouseData mouse, IOHIDValueRef val)
{ {
IOHIDElementRef elem = NativeMethods.IOHIDValueGetElement(val); IOHIDElementRef elem = NativeMethods.IOHIDValueGetElement(val);
int v_int = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32(); int v_int = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32();
@ -237,28 +314,52 @@ namespace OpenTK.Platform.MacOS
switch ((HIDUsageGD)usage) switch ((HIDUsageGD)usage)
{ {
case HIDUsageGD.X: case HIDUsageGD.X:
state.X += v_int; mouse.State.X += v_int;
break; break;
case HIDUsageGD.Y: case HIDUsageGD.Y:
state.Y += v_int; mouse.State.Y += v_int;
break; break;
case HIDUsageGD.Wheel: case HIDUsageGD.Wheel:
state.WheelPrecise += v_int; mouse.State.WheelPrecise += v_int;
break; break;
} }
break; break;
case HIDPage.Button: case HIDPage.Button:
state[OpenTK.Input.MouseButton.Left + usage - 1] = v_int == 1; mouse.State[OpenTK.Input.MouseButton.Left + usage - 1] = v_int == 1;
break; break;
} }
return state;
} }
static KeyboardState UpdateKeyboard(KeyboardState state, IOHIDValueRef val) #endregion
#region Keyboard
void AddKeyboard(CFAllocatorRef sender, CFAllocatorRef device)
{
if (!KeyboardDevices.ContainsKey(device))
{
Debug.Print("Keyboard device {0:x} discovered, sender is {1:x}", device, sender);
KeyboardIndexToDevice.Add(KeyboardDevices.Count, device);
KeyboardDevices.Add(device, new KeyboardData());
}
else
{
Debug.Print("Keyboard device {0:x} reconnected, sender is {1:x}", device, sender);
}
KeyboardDevices[device].State.SetIsConnected(true);
}
void RemoveKeyboard(CFAllocatorRef sender, CFAllocatorRef device)
{
Debug.Print("Keyboard device {0:x} disconnected, sender is {1:x}", device, sender);
// Keep the device in case it comes back later on
KeyboardDevices[device].State.SetIsConnected(false);
}
static void UpdateKeyboard(KeyboardData keyboard, IOHIDValueRef val)
{ {
IOHIDElementRef elem = NativeMethods.IOHIDValueGetElement(val); IOHIDElementRef elem = NativeMethods.IOHIDValueGetElement(val);
int v_int = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32(); int v_int = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32();
@ -267,7 +368,8 @@ namespace OpenTK.Platform.MacOS
// This will supress the debug printing below. Seems like it generates a lot of -1s. // This will supress the debug printing below. Seems like it generates a lot of -1s.
// Couldn't find any details in USB spec or Apple docs for this behavior. // Couldn't find any details in USB spec or Apple docs for this behavior.
if(usage < 0 ) return state; if (usage >= 0)
{
switch (page) switch (page)
{ {
@ -276,14 +378,338 @@ namespace OpenTK.Platform.MacOS
if (usage >= RawKeyMap.Length) if (usage >= RawKeyMap.Length)
{ {
Debug.Print("[Warning] Key {0} not mapped.", usage); Debug.Print("[Warning] Key {0} not mapped.", usage);
return state;
} }
state.SetKeyState(RawKeyMap[usage], (byte)usage, v_int != 0); keyboard.State.SetKeyState(RawKeyMap[usage], (byte)usage, v_int != 0);
break; break;
} }
return state;
} }
}
#endregion
#region Joystick
Guid CreateJoystickGuid(IntPtr device, string name)
{
// Create a device guid from the product and vendor id keys
List<byte> guid_bytes = new List<byte>();
long vendor_id = 0;
long product_id = 0;
IntPtr vendor_id_ref = NativeMethods.IOHIDDeviceGetProperty(device, NativeMethods.IOHIDVendorIDKey);
IntPtr product_id_ref = NativeMethods.IOHIDDeviceGetProperty(device, NativeMethods.IOHIDProductIDKey);
if (vendor_id_ref != IntPtr.Zero)
{
CF.CFNumberGetValue(vendor_id_ref, CF.CFNumberType.kCFNumberLongType, out vendor_id);
}
if (product_id_ref != IntPtr.Zero)
{
CF.CFNumberGetValue(product_id_ref, CF.CFNumberType.kCFNumberLongType, out product_id);
}
if (vendor_id != 0 && product_id != 0)
{
guid_bytes.AddRange(BitConverter.GetBytes(vendor_id));
guid_bytes.AddRange(BitConverter.GetBytes(product_id));
}
else
{
// Bluetooth devices don't have USB vendor/product id keys.
// Match SDL2 algorithm for compatibility.
guid_bytes.Add(0x05); // BUS_BLUETOOTH
guid_bytes.Add(0x00);
guid_bytes.Add(0x00);
guid_bytes.Add(0x00);
// Copy the first 12 bytes of the product name
byte[] name_bytes = new byte[12];
Array.Copy(System.Text.Encoding.UTF8.GetBytes(name), name_bytes, name_bytes.Length);
guid_bytes.AddRange(name_bytes);
}
return new Guid(guid_bytes.ToArray());
}
JoystickData CreateJoystick(IntPtr sender, IntPtr device)
{
JoystickData joy = null;
// Retrieve all elements of this device
CFArrayRef element_array_ref = NativeMethods.IOHIDDeviceCopyMatchingElements(device, IntPtr.Zero, IntPtr.Zero);
if (element_array_ref != IntPtr.Zero)
{
joy = new JoystickData();
int axes = 0;
int buttons = 0;
int dpads = 0;
CFStringRef name_ref = NativeMethods.IOHIDDeviceGetProperty(device, NativeMethods.IOHIDProductKey);
string name = CF.CFStringGetCString(name_ref);
Guid guid = CreateJoystickGuid(device, name);
List<int> button_elements = new List<int>();
CFArray element_array = new CFArray(element_array_ref);
for (int i = 0; i < element_array.Count; i++)
{
IOHIDElementRef element_ref = element_array[i];
IOHIDElementType type = NativeMethods.IOHIDElementGetType(element_ref);
HIDPage page = NativeMethods.IOHIDElementGetUsagePage(element_ref);
int usage = NativeMethods.IOHIDElementGetUsage(element_ref);
switch (page)
{
case HIDPage.GenericDesktop:
switch ((HIDUsageGD)usage)
{
case HIDUsageGD.X:
case HIDUsageGD.Y:
case HIDUsageGD.Z:
case HIDUsageGD.Rx:
case HIDUsageGD.Ry:
case HIDUsageGD.Rz:
case HIDUsageGD.Slider:
case HIDUsageGD.Dial:
case HIDUsageGD.Wheel:
axes++;
break;
case HIDUsageGD.Hatswitch:
dpads++;
break;
}
break;
case HIDPage.Simulation:
switch ((HIDUsageSim)usage)
{
case HIDUsageSim.Rudder:
case HIDUsageSim.Throttle:
axes++;
break;
}
break;
case HIDPage.Button:
button_elements.Add(usage);
break;
}
}
joy.Name = name;
joy.Guid = guid;
joy.State.SetIsConnected(true);
joy.Capabilities = new JoystickCapabilities(axes, buttons, true);
// Map button elements to JoystickButtons
for (int button = 0; button < button_elements.Count; button++)
{
joy.ElementUsageToButton.Add(button_elements[button], JoystickButton.Button0 + button);
}
}
CF.CFRelease(element_array_ref);
return joy;
}
JoystickData GetJoystick(int index)
{
IntPtr device;
if (JoystickIndexToDevice.TryGetValue(index, out device))
{
JoystickData joystick;
if (JoystickDevices.TryGetValue(device, out joystick))
{
return joystick;
}
}
return null;
}
void AddJoystick(CFAllocatorRef sender, CFAllocatorRef device)
{
Debug.Print("Joystick device {0:x} discovered, sender is {1:x}", device, sender);
JoystickData joy = CreateJoystick(sender, device);
if (joy != null)
{
// Add a device->joy lookup entry for this device.
if (!JoystickDevices.ContainsKey(device))
{
// First time we've seen this device.
JoystickDevices.Add(device, joy);
}
else
{
// This is an old device that is replugged.
// This branch does not appear to be executed, ever.
JoystickDevices[device] = joy;
}
// Add an index->device lookup entry for this device.
// Use the first free (i.e. disconnected) index.
// If all indices are connected, append a new one.
int i;
for (i = 0; i < JoystickIndexToDevice.Count; i++)
{
IntPtr candidate = JoystickIndexToDevice[i];
if (!JoystickDevices[candidate].State.IsConnected)
{
break;
}
}
if (i == JoystickDevices.Count)
{
// All indices connected, append a new one.
JoystickIndexToDevice.Add(JoystickDevices.Count, device);
}
else
{
// Replace joystick at that index
JoystickIndexToDevice[i] = device;
}
}
}
void RemoveJoystick(CFAllocatorRef sender, CFAllocatorRef device)
{
Debug.Print("Joystick device {0:x} disconnected, sender is {1:x}", device, sender);
// Keep the device in case it comes back later on
JoystickDevices[device].State = new JoystickState();
JoystickDevices[device].Capabilities = new JoystickCapabilities();
}
static void UpdateJoystick(JoystickData joy, IOHIDValueRef val)
{
IOHIDElementRef elem = NativeMethods.IOHIDValueGetElement(val);
HIDPage page = NativeMethods.IOHIDElementGetUsagePage(elem);
int usage = NativeMethods.IOHIDElementGetUsage(elem);
switch (page)
{
case HIDPage.GenericDesktop:
switch ((HIDUsageGD)usage)
{
case HIDUsageGD.X:
case HIDUsageGD.Y:
case HIDUsageGD.Z:
case HIDUsageGD.Rx:
case HIDUsageGD.Ry:
case HIDUsageGD.Rz:
case HIDUsageGD.Slider:
case HIDUsageGD.Dial:
case HIDUsageGD.Wheel:
short offset = GetJoystickAxis(val, elem);
JoystickAxis axis = TranslateJoystickAxis(usage);
if (axis >= JoystickAxis.Axis0 && axis <= JoystickAxis.Last)
{
joy.State.SetAxis(axis, offset);
}
break;
case HIDUsageGD.Hatswitch:
break;
}
break;
case HIDPage.Simulation:
switch ((HIDUsageSim)usage)
{
case HIDUsageSim.Rudder:
case HIDUsageSim.Throttle:
short offset = GetJoystickAxis(val, elem);
JoystickAxis axis = TranslateJoystickAxis(usage);
if (axis >= JoystickAxis.Axis0 && axis <= JoystickAxis.Last)
{
joy.State.SetAxis(axis, offset);
}
break;
}
break;
case HIDPage.Button:
{
bool pressed = GetJoystickButton(val, elem);
JoystickButton button = TranslateJoystickButton(joy, usage);
if (button >= JoystickButton.Button0 && button <= JoystickButton.Last)
{
joy.State.SetButton(button, pressed);
}
}
break;
}
}
static short GetJoystickAxis(IOHIDValueRef val, IOHIDElementRef element)
{
int max = NativeMethods.IOHIDElementGetLogicalMax(element).ToInt32();
int min = NativeMethods.IOHIDElementGetLogicalMin(element).ToInt32();
int offset = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32();
if (offset < min)
offset = min;
if (offset > max)
offset = max;
const int range = short.MaxValue - short.MinValue + 1;
const int half_range = short.MaxValue + 1;
return (short)((offset - min) * range / (max - min) + half_range);
}
static JoystickAxis TranslateJoystickAxis(int usage)
{
switch (usage)
{
case (int)HIDUsageGD.X:
return JoystickAxis.Axis0;
case (int)HIDUsageGD.Y:
return JoystickAxis.Axis1;
case (int)HIDUsageGD.Z:
return JoystickAxis.Axis2;
case (int)HIDUsageGD.Rz:
return JoystickAxis.Axis3;
case (int)HIDUsageGD.Rx:
return JoystickAxis.Axis4;
case (int)HIDUsageGD.Ry:
return JoystickAxis.Axis5;
case (int)HIDUsageGD.Slider:
return JoystickAxis.Axis6;
case (int)HIDUsageGD.Dial:
return JoystickAxis.Axis7;
case (int)HIDUsageGD.Wheel:
return JoystickAxis.Axis8;
case (int)HIDUsageSim.Rudder:
return JoystickAxis.Axis9;
case (int)HIDUsageSim.Throttle:
return JoystickAxis.Axis10;
default:
Debug.Print("[Mac] Unknown axis with HID usage {0}", usage);
return 0;
}
}
static bool GetJoystickButton(IOHIDValueRef val, IOHIDElementRef element)
{
// Todo: analogue buttons are transformed to digital
int value = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32();
return value >= 1;
}
static JoystickButton TranslateJoystickButton(JoystickData joy, int usage)
{
JoystickButton button;
if (joy.ElementUsageToButton.TryGetValue(usage, out button))
{
return button;
}
return JoystickButton.Last + 1;
}
#endregion
#endregion #endregion
@ -291,7 +717,8 @@ namespace OpenTK.Platform.MacOS
public IMouseDriver2 MouseDriver { get { return this; } } public IMouseDriver2 MouseDriver { get { return this; } }
public IKeyboardDriver2 KeyboardDriver { get { return this; } } public IKeyboardDriver2 KeyboardDriver { get { return this; } }
public IGamePadDriver GamePadDriver { get { throw new NotImplementedException(); } } public IGamePadDriver GamePadDriver { get { return mapped_gamepad; } }
public IJoystickDriver2 JoystickDriver { get { return this; } }
#endregion #endregion
@ -300,9 +727,9 @@ namespace OpenTK.Platform.MacOS
MouseState IMouseDriver2.GetState() MouseState IMouseDriver2.GetState()
{ {
MouseState master = new MouseState(); MouseState master = new MouseState();
foreach (KeyValuePair<IntPtr, MouseState> item in MouseDevices) foreach (KeyValuePair<IntPtr, MouseData> item in MouseDevices)
{ {
master.MergeBits(item.Value); master.MergeBits(item.Value.State);
} }
return master; return master;
@ -313,7 +740,7 @@ namespace OpenTK.Platform.MacOS
IntPtr device; IntPtr device;
if (MouseIndexToDevice.TryGetValue(index, out device)) if (MouseIndexToDevice.TryGetValue(index, out device))
{ {
return MouseDevices[device]; return MouseDevices[device].State;
} }
return new MouseState(); return new MouseState();
@ -332,9 +759,9 @@ namespace OpenTK.Platform.MacOS
KeyboardState IKeyboardDriver2.GetState() KeyboardState IKeyboardDriver2.GetState()
{ {
KeyboardState master = new KeyboardState(); KeyboardState master = new KeyboardState();
foreach (KeyValuePair<IntPtr, KeyboardState> item in KeyboardDevices) foreach (KeyValuePair<IntPtr, KeyboardData> item in KeyboardDevices)
{ {
master.MergeBits(item.Value); master.MergeBits(item.Value.State);
} }
return master; return master;
@ -345,7 +772,7 @@ namespace OpenTK.Platform.MacOS
IntPtr device; IntPtr device;
if (KeyboardIndexToDevice.TryGetValue(index, out device)) if (KeyboardIndexToDevice.TryGetValue(index, out device))
{ {
return KeyboardDevices[device]; return KeyboardDevices[device].State;
} }
return new KeyboardState(); return new KeyboardState();
@ -366,6 +793,40 @@ namespace OpenTK.Platform.MacOS
#endregion #endregion
#region IJoystickDriver2 Members
JoystickState IJoystickDriver2.GetState(int index)
{
JoystickData joystick = GetJoystick(index);
if (joystick != null)
{
return joystick.State;
}
return new JoystickState();
}
JoystickCapabilities IJoystickDriver2.GetCapabilities(int index)
{
JoystickData joystick = GetJoystick(index);
if (joystick != null)
{
return joystick.Capabilities;
}
return new JoystickCapabilities();
}
Guid IJoystickDriver2.GetGuid(int index)
{
JoystickData joystick = GetJoystick(index);
if (joystick != null)
{
return joystick.Guid;
}
return new Guid();
}
#endregion
#region NativeMethods #region NativeMethods
class NativeMethods class NativeMethods
@ -418,6 +879,12 @@ namespace OpenTK.Platform.MacOS
CFRunLoop inCFRunLoop, CFRunLoop inCFRunLoop,
CFString inCFRunLoopMode); CFString inCFRunLoopMode);
[DllImport(hid)]
public static extern void IOHIDManagerUnscheduleFromRunLoop(
IOHIDManagerRef inIOHIDManagerRef,
CFRunLoop inCFRunLoop,
CFString inCFRunLoopMode);
[DllImport(hid)] [DllImport(hid)]
public static extern void IOHIDManagerSetDeviceMatching( public static extern void IOHIDManagerSetDeviceMatching(
IOHIDManagerRef manager, IOHIDManagerRef manager,
@ -444,6 +911,13 @@ namespace OpenTK.Platform.MacOS
HIDPage inUsagePage, // the usage page to test conformance with HIDPage inUsagePage, // the usage page to test conformance with
int inUsage); // the usage to test conformance with int inUsage); // the usage to test conformance with
// return the HID elements that match the criteria contained in the matching dictionary
[DllImport(hid)]
public static extern CFArrayRef IOHIDDeviceCopyMatchingElements(
IOHIDDeviceRef inIOHIDDeviceRef, // IOHIDDeviceRef for the HID device
CFDictionaryRef inMatchingCFDictRef, // the matching dictionary
IOOptionBits inOptions); // Option bits
[DllImport(hid)] [DllImport(hid)]
public static extern void IOHIDDeviceRegisterInputValueCallback( public static extern void IOHIDDeviceRegisterInputValueCallback(
IOHIDDeviceRef device, IOHIDDeviceRef device,
@ -463,7 +937,7 @@ namespace OpenTK.Platform.MacOS
CFString inCFRunLoopMode); CFString inCFRunLoopMode);
[DllImport(hid)] [DllImport(hid)]
public static extern void IOHIDDeviceUnscheduleWithRunLoop( public static extern void IOHIDDeviceUnscheduleFromRunLoop(
IOHIDDeviceRef device, IOHIDDeviceRef device,
CFRunLoop inCFRunLoop, CFRunLoop inCFRunLoop,
CFString inCFRunLoopMode); CFString inCFRunLoopMode);
@ -479,16 +953,37 @@ namespace OpenTK.Platform.MacOS
IOHIDValueRef @value, IOHIDValueRef @value,
IOHIDValueScaleType type) ; IOHIDValueScaleType type) ;
[DllImport(hid)]
public static extern IOHIDElementType IOHIDElementGetType(
IOHIDElementRef element);
[DllImport(hid)] [DllImport(hid)]
public static extern int IOHIDElementGetUsage(IOHIDElementRef elem); public static extern int IOHIDElementGetUsage(IOHIDElementRef elem);
[DllImport(hid)] [DllImport(hid)]
public static extern HIDPage IOHIDElementGetUsagePage(IOHIDElementRef elem); public static extern HIDPage IOHIDElementGetUsagePage(IOHIDElementRef elem);
[DllImport(hid)]
public static extern CFIndex IOHIDElementGetLogicalMax(IOHIDElementRef element);
[DllImport(hid)]
public static extern CFIndex IOHIDElementGetLogicalMin(IOHIDElementRef element);
public delegate void IOHIDDeviceCallback(IntPtr ctx, IOReturn res, IntPtr sender, IOHIDDeviceRef device); public delegate void IOHIDDeviceCallback(IntPtr ctx, IOReturn res, IntPtr sender, IOHIDDeviceRef device);
public delegate void IOHIDValueCallback(IntPtr ctx, IOReturn res, IntPtr sender, IOHIDValueRef val); public delegate void IOHIDValueCallback(IntPtr ctx, IOReturn res, IntPtr sender, IOHIDValueRef val);
} }
enum IOHIDElementType
{
Input_Misc = 1,
Input_Button = 2,
Input_Axis = 3,
Input_ScanCodes = 4,
Output = 129,
Feature = 257,
Collection = 513
}
enum IOHIDValueScaleType enum IOHIDValueScaleType
{ {
Physical, // [device min, device max] Physical, // [device min, device max]
@ -593,6 +1088,65 @@ namespace OpenTK.Platform.MacOS
Reserved = 0xFFFF Reserved = 0xFFFF
} }
enum HIDUsageSim
{
FlightSimulationDevice = 0x01, /* Application Collection */
AutomobileSimulationDevice = 0x02, /* Application Collection */
TankSimulationDevice = 0x03, /* Application Collection */
SpaceshipSimulationDevice = 0x04, /* Application Collection */
SubmarineSimulationDevice = 0x05, /* Application Collection */
SailingSimulationDevice = 0x06, /* Application Collection */
MotorcycleSimulationDevice = 0x07, /* Application Collection */
SportsSimulationDevice = 0x08, /* Application Collection */
AirplaneSimulationDevice = 0x09, /* Application Collection */
HelicopterSimulationDevice = 0x0A, /* Application Collection */
MagicCarpetSimulationDevice = 0x0B, /* Application Collection */
BicycleSimulationDevice = 0x0C, /* Application Collection */
/* 0x0D - 0x1F Reserved */
FlightControlStick = 0x20, /* Application Collection */
FlightStick = 0x21, /* Application Collection */
CyclicControl = 0x22, /* Physical Collection */
CyclicTrim = 0x23, /* Physical Collection */
FlightYoke = 0x24, /* Application Collection */
TrackControl = 0x25, /* Physical Collection */
/* 0x26 - 0xAF Reserved */
Aileron = 0xB0, /* Dynamic Value */
AileronTrim = 0xB1, /* Dynamic Value */
AntiTorqueControl = 0xB2, /* Dynamic Value */
AutopilotEnable = 0xB3, /* On/Off Control */
ChaffRelease = 0xB4, /* One-Shot Control */
CollectiveControl = 0xB5, /* Dynamic Value */
DiveBrake = 0xB6, /* Dynamic Value */
ElectronicCountermeasures = 0xB7, /* On/Off Control */
Elevator = 0xB8, /* Dynamic Value */
ElevatorTrim = 0xB9, /* Dynamic Value */
Rudder = 0xBA, /* Dynamic Value */
Throttle = 0xBB, /* Dynamic Value */
FlightCommunications = 0xBC, /* On/Off Control */
FlareRelease = 0xBD, /* One-Shot Control */
LandingGear = 0xBE, /* On/Off Control */
ToeBrake = 0xBF, /* Dynamic Value */
Trigger = 0xC0, /* Momentary Control */
WeaponsArm = 0xC1, /* On/Off Control */
Weapons = 0xC2, /* Selector */
WingFlaps = 0xC3, /* Dynamic Value */
Accelerator = 0xC4, /* Dynamic Value */
Brake = 0xC5, /* Dynamic Value */
Clutch = 0xC6, /* Dynamic Value */
Shifter = 0xC7, /* Dynamic Value */
Steering = 0xC8, /* Dynamic Value */
TurretDirection = 0xC9, /* Dynamic Value */
BarrelElevation = 0xCA, /* Dynamic Value */
DivePlane = 0xCB, /* Dynamic Value */
Ballast = 0xCC, /* Dynamic Value */
BicycleCrank = 0xCD, /* Dynamic Value */
HandleBars = 0xCE, /* Dynamic Value */
FrontBrake = 0xCF, /* Dynamic Value */
RearBrake = 0xD0, /* Dynamic Value */
/* 0xD1 - 0xFFFF Reserved */
Reserved = 0xFFFF
}
enum HIDButton enum HIDButton
{ {
Button_1 = 0x01, /* (primary/trigger) */ Button_1 = 0x01, /* (primary/trigger) */
@ -981,19 +1535,22 @@ namespace OpenTK.Platform.MacOS
hidmanager, IntPtr.Zero, IntPtr.Zero); hidmanager, IntPtr.Zero, IntPtr.Zero);
NativeMethods.IOHIDManagerRegisterDeviceRemovalCallback( NativeMethods.IOHIDManagerRegisterDeviceRemovalCallback(
hidmanager, IntPtr.Zero, IntPtr.Zero); hidmanager, IntPtr.Zero, IntPtr.Zero);
NativeMethods.IOHIDManagerScheduleWithRunLoop( NativeMethods.IOHIDManagerUnscheduleFromRunLoop(
hidmanager, IntPtr.Zero, IntPtr.Zero); hidmanager, RunLoop, InputLoopMode);
foreach (var device in MouseDevices.Keys) foreach (var device in MouseDevices.Keys)
{ {
NativeMethods.IOHIDDeviceRegisterInputValueCallback( DeviceRemoved(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, device);
device, IntPtr.Zero, IntPtr.Zero);
} }
foreach (var device in KeyboardDevices.Keys) foreach (var device in KeyboardDevices.Keys)
{ {
NativeMethods.IOHIDDeviceRegisterInputValueCallback( DeviceRemoved(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, device);
device, IntPtr.Zero, IntPtr.Zero); }
foreach (var device in JoystickDevices.Keys)
{
DeviceRemoved(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, device);
} }
HandleDeviceAdded = null; HandleDeviceAdded = null;

View file

@ -70,7 +70,7 @@ namespace OpenTK.Platform.MacOS
{ {
return (GraphicsContext.GetCurrentContextDelegate)delegate return (GraphicsContext.GetCurrentContextDelegate)delegate
{ {
return new ContextHandle(Agl.aglGetCurrentContext()); return new ContextHandle(Cgl.GetCurrentContext());
}; };
} }
@ -94,6 +94,11 @@ namespace OpenTK.Platform.MacOS
return InputDriver.GamePadDriver; return InputDriver.GamePadDriver;
} }
public IJoystickDriver2 CreateJoystickDriver()
{
return InputDriver.JoystickDriver;
}
#endregion #endregion
#region IDisposable Members #region IDisposable Members

View file

@ -0,0 +1,92 @@
#region License
//
// NS.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-2013 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;
using System.Runtime.InteropServices;
namespace OpenTK.Platform.MacOS
{
internal class NS
{
const string Library = "libdl.dylib";
[DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")]
static extern bool NSIsSymbolNameDefined(string s);
[DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")]
static extern bool NSIsSymbolNameDefined(IntPtr s);
[DllImport(Library, EntryPoint = "NSLookupAndBindSymbol")]
static extern IntPtr NSLookupAndBindSymbol(string s);
[DllImport(Library, EntryPoint = "NSLookupAndBindSymbol")]
static extern IntPtr NSLookupAndBindSymbol(IntPtr s);
[DllImport(Library, EntryPoint = "NSAddressOfSymbol")]
static extern IntPtr NSAddressOfSymbol(IntPtr symbol);
public static IntPtr GetAddress(string function)
{
// Instead of allocating and combining strings in managed memory
// we do that directly in unmanaged memory. This way, we avoid
// 2 string allocations every time this function is called.
// must add a '_' prefix and null-terminate the function name,
// hence we allocate +2 bytes
IntPtr ptr = Marshal.AllocHGlobal(function.Length + 2);
try
{
Marshal.WriteByte(ptr, (byte)'_');
for (int i = 0; i < function.Length; i++)
{
Marshal.WriteByte(ptr, i + 1, (byte)function[i]);
}
Marshal.WriteByte(ptr, function.Length + 1, 0); // null-terminate
IntPtr symbol = GetAddress(ptr);
return symbol;
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
public static IntPtr GetAddress(IntPtr function)
{
IntPtr symbol = IntPtr.Zero;
if (NSIsSymbolNameDefined(function))
{
symbol = NSLookupAndBindSymbol(function);
if (symbol != IntPtr.Zero)
symbol = NSAddressOfSymbol(symbol);
}
return symbol;
}
}
}

View file

@ -0,0 +1,196 @@
#region License
//
// MappedGamePadDriver.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-2013 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;
using System.Collections.Generic;
using System.Text;
using OpenTK.Input;
namespace OpenTK.Platform
{
/// \internal
/// <summary>
/// Implements IGamePadDriver using OpenTK.Input.Joystick
/// and a gamepad-specific axis/button mapping.
/// </summary>
/// <remarks>
/// <para>
/// This class supports OpenTK and is not meant to be accessed by user code.
/// </para>
/// <para>
/// To support gamepads on platforms that do not offer a gamepad-optimized API,
/// we need to use the generic OpenTK.Input.Joystick and implement a custom
/// mapping scheme to provide a stable mapping to OpenTK.Input.GamePad. This
/// class implements this mapping scheme.
/// </para>
/// </remarks>
class MappedGamePadDriver : IGamePadDriver
{
readonly GamePadConfigurationDatabase database =
new GamePadConfigurationDatabase();
readonly Dictionary<Guid, GamePadConfiguration> configurations =
new Dictionary<Guid, GamePadConfiguration>();
public GamePadState GetState(int index)
{
JoystickState joy = Joystick.GetState(index);
GamePadState pad = new GamePadState();
if (joy.IsConnected)
{
pad.SetConnected(true);
pad.SetPacketNumber(joy.PacketNumber);
GamePadConfiguration configuration = GetConfiguration(Joystick.GetGuid(index));
foreach (GamePadConfigurationItem map in configuration)
{
switch (map.Source.Type)
{
case ConfigurationType.Axis:
{
// JoystickAxis -> Buttons/GamePadAxes mapping
JoystickAxis source_axis = map.Source.Axis;
GamePadAxes target_axis = map.Target.Axis;
short value = joy.GetAxisRaw(source_axis);
switch (map.Target.Type)
{
case ConfigurationType.Axis:
pad.SetAxis(target_axis, value);
break;
case ConfigurationType.Button:
throw new NotImplementedException();
break;
}
}
break;
case ConfigurationType.Button:
{
// JoystickButton -> Buttons/GamePadAxes mapping
JoystickButton source_button = map.Source.Button;
Buttons target_button = map.Target.Button;
bool pressed = joy.GetButton(source_button) == ButtonState.Pressed;
switch (map.Target.Type)
{
case ConfigurationType.Axis:
throw new NotImplementedException();
break;
case ConfigurationType.Button:
pad.SetButton(target_button, pressed);
break;
}
}
break;
}
}
}
return pad;
}
public GamePadCapabilities GetCapabilities(int index)
{
JoystickCapabilities joy = Joystick.GetCapabilities(index);
GamePadCapabilities pad;
if (joy.IsConnected)
{
GamePadConfiguration configuration = GetConfiguration(Joystick.GetGuid(index));
GamePadAxes mapped_axes = 0;
Buttons mapped_buttons = 0;
foreach (GamePadConfigurationItem map in configuration)
{
switch (map.Target.Type)
{
case ConfigurationType.Axis:
mapped_axes |= map.Target.Axis;
break;
case ConfigurationType.Button:
mapped_buttons |= map.Target.Button;
break;
}
}
pad = new GamePadCapabilities(
GamePadType.GamePad, // Todo: detect different types
mapped_axes,
mapped_buttons,
true);
}
else
{
pad = new GamePadCapabilities();
}
return pad;
}
public string GetName(int index)
{
JoystickCapabilities joy = Joystick.GetCapabilities(index);
string name = String.Empty;
if (joy.IsConnected)
{
GamePadConfiguration map = GetConfiguration(Joystick.GetGuid(index));
name = map.Name;
}
return name;
}
public bool SetVibration(int index, float left, float right)
{
return false;
}
#region Private Members
GamePadConfiguration GetConfiguration(Guid guid)
{
if (!configurations.ContainsKey(guid))
{
string config = database[guid];
GamePadConfiguration map = new GamePadConfiguration(config);
configurations.Add(guid, map);
}
return configurations[guid];
}
bool IsMapped(GamePadConfigurationSource item)
{
return item.Type != ConfigurationType.Unmapped;
}
#endregion
}
}

View file

@ -23,8 +23,6 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE. // OTHER DEALINGS IN THE SOFTWARE.
// //
using System.Runtime.InteropServices;
#endregion #endregion
@ -96,7 +94,6 @@ namespace OpenTK.Platform.SDL2
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_CreateWindow", ExactSpelling = true)] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_CreateWindow", ExactSpelling = true)]
public static extern IntPtr CreateWindow(string title, int x, int y, int w, int h, WindowFlags flags); public static extern IntPtr CreateWindow(string title, int x, int y, int w, int h, WindowFlags flags);
//public static extern IntPtr SDL_CreateWindow(string title, int x, int y, int w, int h, WindowFlags flags);
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_CreateWindowFrom", ExactSpelling = true)] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_CreateWindowFrom", ExactSpelling = true)]
@ -118,10 +115,91 @@ namespace OpenTK.Platform.SDL2
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_FreeSurface", ExactSpelling = true)] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_FreeSurface", ExactSpelling = true)]
public static extern void FreeSurface(IntPtr surface); public static extern void FreeSurface(IntPtr surface);
#region GameContoller
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerEventState", ExactSpelling = true)]
public static extern EventState GameControllerEventState(EventState state);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerGetAxis", ExactSpelling = true)]
public static extern short GameControllerGetAxis(IntPtr gamecontroller, GameControllerAxis axis);
/// <summary>
/// Gets the SDL joystick layer binding for the specified game controller axis
/// </summary>
/// <param name="gamecontroller">Pointer to a game controller instance returned by <c>GameControllerOpen</c>.</param>
/// <param name="axis">A value from the <c>GameControllerAxis</c> enumeration</param>
/// <returns>A GameControllerButtonBind instance describing the specified binding</returns>
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerGetBindForAxis", ExactSpelling = true)]
public static extern GameControllerButtonBind GameControllerGetBindForAxis(IntPtr gamecontroller, GameControllerAxis axis);
/// <summary>
/// Gets the SDL joystick layer binding for the specified game controller button
/// </summary>
/// <param name="gamecontroller">Pointer to a game controller instance returned by <c>GameControllerOpen</c>.</param>
/// <param name="button">A value from the <c>GameControllerButton</c> enumeration</param>
/// <returns>A GameControllerButtonBind instance describing the specified binding</returns>
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerGetBindForButton", ExactSpelling = true)]
public static extern GameControllerButtonBind GameControllerGetBindForButton(
IntPtr gamecontroller, GameControllerButton button);
/// <summary>
/// Gets the current state of a button on a game controller.
/// </summary>
/// <param name="gamecontroller">A game controller handle previously opened with <c>GameControllerOpen</c>.</param>
/// <param name="button">A zero-based <c>GameControllerButton</c> value.</param>
/// <returns><c>true</c> if the specified button is pressed; <c>false</c> otherwise.</returns>
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerGetButton", ExactSpelling = true)]
public static extern bool GameControllerGetButton(IntPtr gamecontroller, GameControllerButton button);
/// <summary>
/// Retrieve the joystick handle that corresponds to the specified game controller.
/// </summary>
/// <param name="gamecontroller">A game controller handle previously opened with <c>GameControllerOpen</c>.</param>
/// <returns>A handle to a joystick, or IntPtr.Zero in case of error. The pointer is owned by the callee. Use <c>SDL.GetError</c> to retrieve error information</returns>
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerGetJoystick", ExactSpelling = true)]
public static extern IntPtr GameControllerGetJoystick(IntPtr gamecontroller);
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetCurrentDisplayMode", ExactSpelling = true)] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetCurrentDisplayMode", ExactSpelling = true)]
public static extern int GetCurrentDisplayMode(int displayIndex, out DisplayMode mode); public static extern int GetCurrentDisplayMode(int displayIndex, out DisplayMode mode);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerName", ExactSpelling = true)]
static extern IntPtr GameControllerNameInternal(IntPtr gamecontroller);
/// <summary>
/// Return the name for an openend game controller instance.
/// </summary>
/// <returns>The name of the game controller name.</returns>
/// <param name="gamecontroller">Pointer to a game controller instance returned by <c>GameControllerOpen</c>.</param>
public static string GameControllerName(IntPtr gamecontroller)
{
unsafe
{
return new string((sbyte*)GameControllerNameInternal(gamecontroller));
}
}
/// <summary>
/// Opens a game controller for use.
/// </summary>
/// <param name="joystick_index">
/// A zero-based index for the game controller.
/// This index is the value which will identify this controller in future controller events.
/// </param>
/// <returns>A handle to the game controller instance, or IntPtr.Zero in case of error.</returns>
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerOpen", ExactSpelling = true)]
public static extern IntPtr GameControllerOpen(int joystick_index);
#endregion
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetDisplayBounds", ExactSpelling = true)] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetDisplayBounds", ExactSpelling = true)]
public static extern int GetDisplayBounds(int displayIndex, out Rect rect); public static extern int GetDisplayBounds(int displayIndex, out Rect rect);
@ -192,10 +270,27 @@ namespace OpenTK.Platform.SDL2
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_Init", ExactSpelling = true)] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_Init", ExactSpelling = true)]
public static extern int Init(SystemFlags flags); public static extern int Init(SystemFlags flags);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_InitSubSystem", ExactSpelling = true)]
public static extern int InitSubSystem(SystemFlags flags);
/// <summary>
/// Determines if the specified joystick is supported by the GameController API.
/// </summary>
/// <returns><c>true</c> if joystick_index is supported by the GameController API; <c>false</c> otherwise.</returns>
/// <param name="joystick_index">The index of the joystick to check.</param>
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_IsGameController", ExactSpelling = true)]
public static extern bool IsGameController(int joystick_index);
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickClose", ExactSpelling = true)] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickClose", ExactSpelling = true)]
public static extern void JoystickClose(IntPtr joystick); public static extern void JoystickClose(IntPtr joystick);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickEventState", ExactSpelling = true)]
public static extern EventState JoystickEventState(EventState enabled);
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickGetAxis", ExactSpelling = true)] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickGetAxis", ExactSpelling = true)]
public static extern short JoystickGetAxis(IntPtr joystick, int axis); public static extern short JoystickGetAxis(IntPtr joystick, int axis);
@ -204,6 +299,10 @@ namespace OpenTK.Platform.SDL2
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickGetButton", ExactSpelling = true)] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickGetButton", ExactSpelling = true)]
public static extern byte JoystickGetButton(IntPtr joystick, int button); public static extern byte JoystickGetButton(IntPtr joystick, int button);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickGetGUID", ExactSpelling = true)]
public static extern JoystickGuid JoystickGetGUID(IntPtr joystick);
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickName", ExactSpelling = true)] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickName", ExactSpelling = true)]
static extern IntPtr JoystickNameInternal(IntPtr joystick); static extern IntPtr JoystickNameInternal(IntPtr joystick);
@ -320,6 +419,34 @@ namespace OpenTK.Platform.SDL2
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_WarpMouseInWindow", ExactSpelling = true)] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_WarpMouseInWindow", ExactSpelling = true)]
public static extern void WarpMouseInWindow(IntPtr window, int x, int y); public static extern void WarpMouseInWindow(IntPtr window, int x, int y);
#region SysWM
/// <summary>
/// Retrieves driver-dependent window information.
/// </summary>
/// <param name="window">
/// The window about which information is being requested.
/// </param>
/// <param name="info">
/// Returns driver-dependent information about the specified window.
/// </param>
/// <returns>
/// True, if the function is implemented and the version number of the info struct is valid;
/// false, otherwise.
/// </returns>
public static bool GetWindowWMInfo(IntPtr window, out SysWMInfo info)
{
info = new SysWMInfo();
info.Version = GetVersion();
return GetWindowWMInfoInternal(window, ref info);
}
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetWindowWMInfo", ExactSpelling = true)]
static extern bool GetWindowWMInfoInternal(IntPtr window, ref SysWMInfo info);
#endregion
public partial class GL public partial class GL
{ {
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
@ -334,6 +461,7 @@ namespace OpenTK.Platform.SDL2
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetAttribute", ExactSpelling = true)] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetAttribute", ExactSpelling = true)]
public static extern int GetAttribute(ContextAttribute attr, out int value); public static extern int GetAttribute(ContextAttribute attr, out int value);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetCurrentContext", ExactSpelling = true)] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetCurrentContext", ExactSpelling = true)]
public static extern IntPtr GetCurrentContext(); public static extern IntPtr GetCurrentContext();
@ -461,6 +589,13 @@ namespace OpenTK.Platform.SDL2
ES = 0x0004 ES = 0x0004
} }
enum EventState
{
Query = -1,
Ignore = 0,
Enable = 1
}
enum EventType enum EventType
{ {
FIRSTEVENT = 0, FIRSTEVENT = 0,
@ -500,6 +635,61 @@ namespace OpenTK.Platform.SDL2
LASTEVENT = 0xFFFF LASTEVENT = 0xFFFF
} }
enum GameControllerAxis : byte
{
Invalid = 0xff,
LeftX = 0,
LeftY,
RightX,
RightY,
TriggerLeft,
TriggerRight,
Max
}
enum GameControllerButton : byte
{
INVALID = 0xff,
A = 0,
B,
X,
Y,
BACK,
GUIDE,
START,
LEFTSTICK,
RIGHTSTICK,
LEFTSHOULDER,
RIGHTSHOULDER,
DPAD_UP,
DPAD_DOWN,
DPAD_LEFT,
DPAD_RIGHT,
Max
}
enum GameControllerBindType : byte
{
None = 0,
Button,
Axis,
Hat
}
[Flags]
enum HatPosition : byte
{
Centered = 0x00,
Up = 0x01,
Right = 0x02,
Down = 0x03,
Left = 0x04,
RightUp = Right | Up,
RightDown = Right | Down,
LeftUp = Left | Up,
LeftDown = Left | Down
}
enum Keycode enum Keycode
{ {
UNKNOWN = 0, UNKNOWN = 0,
@ -1037,6 +1227,17 @@ namespace OpenTK.Platform.SDL2
JOYSTICK | HAPTIC | GAMECONTROLLER JOYSTICK | HAPTIC | GAMECONTROLLER
} }
enum SysWMType
{
Unknown = 0,
Windows,
X11,
Wayland,
DirectFB,
Cocoa,
UIKit,
}
enum WindowEventID : byte enum WindowEventID : byte
{ {
NONE, NONE,
@ -1079,6 +1280,41 @@ namespace OpenTK.Platform.SDL2
#region Structs #region Structs
struct ControllerAxisEvent
{
public EventType Type;
public uint Timestamp;
public int Which;
public GameControllerAxis Axis;
byte padding1;
byte padding2;
byte padding3;
public short Value;
ushort padding4;
}
struct ControllerButtonEvent
{
public EventType Type;
public uint Timestamp;
public int Which;
public GameControllerButton Button;
public State State;
byte padding1;
byte padding2;
}
struct ControllerDeviceEvent
{
public EventType Type;
public uint Timestamp;
/// <summary>
/// The joystick device index for the ADDED event, instance id for the REMOVED or REMAPPED event
/// </summary>
public int Which;
}
struct DisplayMode struct DisplayMode
{ {
public uint Format; public uint Format;
@ -1109,21 +1345,21 @@ namespace OpenTK.Platform.SDL2
public MouseWheelEvent Wheel; public MouseWheelEvent Wheel;
[FieldOffset(0)] [FieldOffset(0)]
public JoyAxisEvent JoyAxis; public JoyAxisEvent JoyAxis;
[FieldOffset(0)]
public JoyBallEvent JoyBall;
[FieldOffset(0)]
public JoyHatEvent JoyHat;
[FieldOffset(0)]
public JoyButtonEvent JoyButton;
[FieldOffset(0)]
public JoyDeviceEvent JoyDevice;
[FieldOffset(0)]
public ControllerAxisEvent ControllerAxis;
[FieldOffset(0)]
public ControllerButtonEvent ControllerButton;
[FieldOffset(0)]
public ControllerDeviceEvent ControllerDevice;
#if false #if false
[FieldOffset(0)]
public JoyBallEvent jball;
[FieldOffset(0)]
public JoyHatEvent jhat;
[FieldOffset(0)]
public JoyButtonEvent jbutton;
[FieldOffset(0)]
public JoyDeviceEvent jdevice;
[FieldOffset(0)]
public ControllerAxisEvent caxis;
[FieldOffset(0)]
public ControllerButtonEvent cbutton;
[FieldOffset(0)]
public ControllerDeviceEvent cdevice;
[FieldOffset(0)] [FieldOffset(0)]
public QuitEvent quit; public QuitEvent quit;
[FieldOffset(0)] [FieldOffset(0)]
@ -1141,6 +1377,21 @@ namespace OpenTK.Platform.SDL2
#endif #endif
} }
[StructLayout(LayoutKind.Explicit)]
struct GameControllerButtonBind
{
[FieldOffset(0)]
public GameControllerBindType BindType;
[FieldOffset(4)]
public Button Button;
[FieldOffset(4)]
public GameControllerAxis Axis;
[FieldOffset(4)]
public int Hat;
[FieldOffset(8)]
public int HatMask;
}
struct JoyAxisEvent struct JoyAxisEvent
{ {
public EventType Type; public EventType Type;
@ -1154,6 +1405,68 @@ namespace OpenTK.Platform.SDL2
UInt16 padding4; UInt16 padding4;
} }
struct JoyBallEvent
{
public EventType Type;
public uint Timestamp;
public int Which;
public byte Ball;
byte padding1;
byte padding2;
byte padding3;
public short Xrel;
public short Yrel;
}
struct JoyButtonEvent
{
public EventType Type;
public uint Timestamp;
public int Which;
public byte Button;
public State State;
byte padding1;
byte padding2;
}
struct JoyDeviceEvent
{
public EventType Type;
public uint Timestamp;
public int Which;
}
struct JoyHatEvent
{
public EventType Type;
public uint Timestamp;
public int Which;
public byte Hat;
public HatPosition Value;
byte padding1;
byte padding2;
}
struct JoystickGuid
{
unsafe fixed byte data[16];
public Guid ToGuid()
{
byte[] bytes = new byte[16];
unsafe
{
fixed (byte* pdata = data)
{
Marshal.Copy(new IntPtr(pdata), bytes, 0, bytes.Length);
}
}
return new Guid(bytes);
}
}
struct KeyboardEvent struct KeyboardEvent
{ {
public EventType Type; public EventType Type;
@ -1242,6 +1555,65 @@ namespace OpenTK.Platform.SDL2
public int Height; public int Height;
} }
struct SysWMInfo
{
public Version Version;
public SysWMType Subsystem;
public SysInfo Info;
[StructLayout(LayoutKind.Explicit)]
public struct SysInfo
{
[FieldOffset(0)]
public WindowsInfo Windows;
[FieldOffset(0)]
public X11Info X11;
[FieldOffset(0)]
public WaylandInfo Wayland;
[FieldOffset(0)]
public DirectFBInfo DirectFB;
[FieldOffset(0)]
public CocoaInfo Cocoa;
[FieldOffset(0)]
public UIKitInfo UIKit;
public struct WindowsInfo
{
public IntPtr Window;
}
public struct X11Info
{
public IntPtr Display;
public IntPtr Window;
}
public struct WaylandInfo
{
public IntPtr Display;
public IntPtr Surface;
public IntPtr ShellSurface;
}
public struct DirectFBInfo
{
public IntPtr Dfb;
public IntPtr Window;
public IntPtr Surface;
}
public struct CocoaInfo
{
public IntPtr Window;
}
public struct UIKitInfo
{
public IntPtr Window;
}
}
}
struct TextEditingEvent struct TextEditingEvent
{ {
public const int TextSize = 32; public const int TextSize = 32;

View file

@ -34,7 +34,7 @@ namespace OpenTK.Platform.SDL2
{ {
class Sdl2Factory : IPlatformFactory class Sdl2Factory : IPlatformFactory
{ {
readonly IInputDriver2 InputDriver = new Sdl2InputDriver(); readonly Sdl2InputDriver InputDriver = new Sdl2InputDriver();
bool disposed; bool disposed;
/// <summary> /// <summary>
@ -58,7 +58,7 @@ namespace OpenTK.Platform.SDL2
public INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) public INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device)
{ {
return new Sdl2NativeWindow(x, y, width, height, title, options, device); return new Sdl2NativeWindow(x, y, width, height, title, options, device, InputDriver);
} }
public IDisplayDeviceDriver CreateDisplayDeviceDriver() public IDisplayDeviceDriver CreateDisplayDeviceDriver()
@ -104,6 +104,11 @@ namespace OpenTK.Platform.SDL2
return InputDriver.GamePadDriver; return InputDriver.GamePadDriver;
} }
public IJoystickDriver2 CreateJoystickDriver()
{
return InputDriver.JoystickDriver;
}
#endregion #endregion
#region IDisposable Members #region IDisposable Members

View file

@ -157,6 +157,8 @@ namespace OpenTK.Platform.SDL2
int major, int minor, int major, int minor,
GraphicsContextFlags flags) GraphicsContextFlags flags)
{ {
ContextProfileFlags cpflags = 0;
if (mode.AccumulatorFormat.BitsPerPixel > 0) if (mode.AccumulatorFormat.BitsPerPixel > 0)
{ {
SDL.GL.SetAttribute(ContextAttribute.ACCUM_ALPHA_SIZE, mode.AccumulatorFormat.Alpha); SDL.GL.SetAttribute(ContextAttribute.ACCUM_ALPHA_SIZE, mode.AccumulatorFormat.Alpha);
@ -203,6 +205,15 @@ namespace OpenTK.Platform.SDL2
{ {
SDL.GL.SetAttribute(ContextAttribute.CONTEXT_MAJOR_VERSION, major); SDL.GL.SetAttribute(ContextAttribute.CONTEXT_MAJOR_VERSION, major);
SDL.GL.SetAttribute(ContextAttribute.CONTEXT_MINOR_VERSION, minor); SDL.GL.SetAttribute(ContextAttribute.CONTEXT_MINOR_VERSION, minor);
// Workaround for https://github.com/opentk/opentk/issues/44
// Mac OS X desktop OpenGL 3.x/4.x contexts require require
// ContextProfileFlags.Core, otherwise they will fail to construct.
if (Configuration.RunningOnMacOS && major >= 3 &&
(flags & GraphicsContextFlags.Embedded) == 0)
{
cpflags |= ContextProfileFlags.CORE;
}
} }
if ((flags & GraphicsContextFlags.Debug) != 0) if ((flags & GraphicsContextFlags.Debug) != 0)
@ -223,7 +234,6 @@ namespace OpenTK.Platform.SDL2
*/ */
{ {
ContextProfileFlags cpflags = 0;
if ((flags & GraphicsContextFlags.Embedded) != 0) if ((flags & GraphicsContextFlags.Embedded) != 0)
{ {
cpflags |= ContextProfileFlags.ES; cpflags |= ContextProfileFlags.ES;

View file

@ -55,10 +55,21 @@ namespace OpenTK.Platform.SDL2
{ {
lock (SDL.Sync) lock (SDL.Sync)
{ {
SDL.GameControllerEventState(EventState.Enable);
SDL.JoystickEventState(EventState.Enable);
EventFilterDelegate = Marshal.GetFunctionPointerForDelegate(EventFilterDelegate_GCUnsafe); EventFilterDelegate = Marshal.GetFunctionPointerForDelegate(EventFilterDelegate_GCUnsafe);
driver_handle = new IntPtr(count++); driver_handle = new IntPtr(count++);
DriverHandles.Add(driver_handle, this); DriverHandles.Add(driver_handle, this);
SDL.AddEventWatch(EventFilterDelegate, driver_handle); SDL.AddEventWatch(EventFilterDelegate, driver_handle);
if (SDL.InitSubSystem(SystemFlags.JOYSTICK) < 0)
{
Debug.Print("[SDL2] InputDriver failed to init Joystick subsystem. Error: {0}", SDL.GetError());
}
if (SDL.InitSubSystem(SystemFlags.GAMECONTROLLER) < 0)
{
Debug.Print("[SDL2] InputDriver failed to init GameController subsystem. Error: {0}", SDL.GetError());
}
} }
} }
@ -92,6 +103,44 @@ namespace OpenTK.Platform.SDL2
case EventType.MOUSEWHEEL: case EventType.MOUSEWHEEL:
driver.mouse_driver.ProcessWheelEvent(ev.Wheel); driver.mouse_driver.ProcessWheelEvent(ev.Wheel);
break; break;
case EventType.JOYDEVICEADDED:
case EventType.JOYDEVICEREMOVED:
driver.joystick_driver.ProcessJoystickEvent(ev.JoyDevice);
break;
case EventType.JOYAXISMOTION:
driver.joystick_driver.ProcessJoystickEvent(ev.JoyAxis);
break;
case EventType.JOYBALLMOTION:
driver.joystick_driver.ProcessJoystickEvent(ev.JoyBall);
break;
case EventType.JOYBUTTONDOWN:
case EventType.JOYBUTTONUP:
driver.joystick_driver.ProcessJoystickEvent(ev.JoyButton);
break;
case EventType.JOYHATMOTION:
driver.joystick_driver.ProcessJoystickEvent(ev.JoyHat);
break;
#if USE_SDL2_GAMECONTROLLER
case EventType.CONTROLLERDEVICEADDED:
case EventType.CONTROLLERDEVICEREMOVED:
driver.joystick_driver.ProcessControllerEvent(ev.ControllerDevice);
break;
case EventType.CONTROLLERAXISMOTION:
driver.joystick_driver.ProcessControllerEvent(ev.ControllerAxis);
break;
case EventType.CONTROLLERBUTTONDOWN:
case EventType.CONTROLLERBUTTONUP:
driver.joystick_driver.ProcessControllerEvent(ev.ControllerButton);
break;
#endif
} }
} }
} }
@ -172,7 +221,15 @@ namespace OpenTK.Platform.SDL2
{ {
get get
{ {
throw new NotImplementedException(); return joystick_driver;
}
}
public IJoystickDriver2 JoystickDriver
{
get
{
return joystick_driver;
} }
} }

View file

@ -32,36 +32,56 @@ using OpenTK.Input;
namespace OpenTK.Platform.SDL2 namespace OpenTK.Platform.SDL2
{ {
class Sdl2JoystickDriver : IJoystickDriver, IGamePadDriver, IDisposable class Sdl2JoystickDriver : IJoystickDriver, IJoystickDriver2, IGamePadDriver, IDisposable
{ {
struct Sdl2JoystickDetails const float RangeMultiplier = 1.0f / 32768.0f;
readonly MappedGamePadDriver gamepad_driver = new MappedGamePadDriver();
bool disposed;
class Sdl2JoystickDetails
{ {
public IntPtr Handle { get; set; } public IntPtr Handle { get; set; }
public float RangeMultiplier { get { return 1.0f / 32768.0f; } } public Guid Guid { get; set; }
public int PacketNumber { get; set; }
public int HatCount { get; set; } public int HatCount { get; set; }
public int BallCount { get; set; } public int BallCount { get; set; }
public bool IsConnected { get; set; }
} }
readonly List<JoystickDevice> joysticks = new List<JoystickDevice>(); // For IJoystickDriver2 implementation
int last_joystick_instance = 0;
readonly List<JoystickDevice> joysticks = new List<JoystickDevice>(4);
readonly Dictionary<int, int> sdl_instanceid_to_joysticks = new Dictionary<int, int>();
// For IJoystickDriver implementation
IList<JoystickDevice> joysticks_readonly; IList<JoystickDevice> joysticks_readonly;
bool disposed = false;
#if USE_SDL2_GAMECONTROLLER
class Sdl2GamePad
{
public IntPtr Handle { get; private set; }
public GamePadState State;
public GamePadCapabilities Capabilities;
public Sdl2GamePad(IntPtr handle)
{
Handle = handle;
}
}
int last_controllers_instance = 0;
readonly List<Sdl2GamePad> controllers = new List<Sdl2GamePad>(4);
readonly Dictionary<int, int> sdl_instanceid_to_controllers = new Dictionary<int, int>();
#endif
public Sdl2JoystickDriver() public Sdl2JoystickDriver()
{ {
joysticks_readonly = joysticks.AsReadOnly(); joysticks_readonly = joysticks.AsReadOnly();
RefreshJoysticks();
} }
#region Private Members #region Private Members
void RefreshJoysticks() JoystickDevice<Sdl2JoystickDetails> OpenJoystick(int id)
{
joysticks.Clear();
int count = SDL.NumJoysticks();
for (int i = 0; i < count; i++)
{ {
JoystickDevice<Sdl2JoystickDetails> joystick = null; JoystickDevice<Sdl2JoystickDetails> joystick = null;
int num_axes = 0; int num_axes = 0;
@ -69,7 +89,7 @@ namespace OpenTK.Platform.SDL2
int num_hats = 0; int num_hats = 0;
int num_balls = 0; int num_balls = 0;
IntPtr handle = SDL.JoystickOpen(i); IntPtr handle = SDL.JoystickOpen(id);
if (handle != IntPtr.Zero) if (handle != IntPtr.Zero)
{ {
num_axes = SDL.JoystickNumAxes(handle); num_axes = SDL.JoystickNumAxes(handle);
@ -77,15 +97,416 @@ namespace OpenTK.Platform.SDL2
num_hats = SDL.JoystickNumHats(handle); num_hats = SDL.JoystickNumHats(handle);
num_balls = SDL.JoystickNumBalls(handle); num_balls = SDL.JoystickNumBalls(handle);
joystick = new JoystickDevice<Sdl2JoystickDetails>(i, num_axes, num_buttons); joystick = new JoystickDevice<Sdl2JoystickDetails>(id, num_axes, num_buttons);
joystick.Description = SDL.JoystickName(handle); joystick.Description = SDL.JoystickName(handle);
joystick.Details.Handle = handle; joystick.Details.Handle = handle;
joystick.Details.Guid = SDL.JoystickGetGUID(handle).ToGuid();
joystick.Details.HatCount = num_hats; joystick.Details.HatCount = num_hats;
joystick.Details.BallCount = num_balls; joystick.Details.BallCount = num_balls;
Debug.Print("[SDL2] Joystick device {0} opened successfully. ", id);
Debug.Print("\t\t'{0}' has {1} axes, {2} buttons, {3} hats, {4} balls",
joystick.Description, joystick.Axis.Count, joystick.Button.Count,
joystick.Details.HatCount, joystick.Details.BallCount);
}
else
{
Debug.Print("[SDL2] Failed to open joystick device {0}", id);
}
return joystick;
}
bool IsJoystickValid(int id)
{
return id >= 0 && id < joysticks.Count;
}
bool IsJoystickInstanceValid(int instance_id)
{
return sdl_instanceid_to_joysticks.ContainsKey(instance_id);
}
#if USE_SDL2_GAMECONTROLLER
bool IsControllerValid(int id)
{
return id >= 0 && id < controllers.Count;
}
bool IsControllerInstanceValid(int instance_id)
{
return sdl_instanceid_to_controllers.ContainsKey(instance_id);
}
GamePadAxes GetBoundAxes(IntPtr gamecontroller)
{
GamePadAxes axes = 0;
axes |= IsAxisBind(gamecontroller, GameControllerAxis.LeftX) ? GamePadAxes.LeftX : 0;
axes |= IsAxisBind(gamecontroller, GameControllerAxis.LeftY) ? GamePadAxes.LeftY : 0;
axes |= IsAxisBind(gamecontroller, GameControllerAxis.RightX) ? GamePadAxes.RightX : 0;
axes |= IsAxisBind(gamecontroller, GameControllerAxis.RightY) ? GamePadAxes.RightY : 0;
axes |= IsAxisBind(gamecontroller, GameControllerAxis.TriggerLeft) ? GamePadAxes.LeftTrigger : 0;
axes |= IsAxisBind(gamecontroller, GameControllerAxis.TriggerRight) ? GamePadAxes.RightTrigger : 0;
return axes;
}
Buttons GetBoundButtons(IntPtr gamecontroller)
{
Buttons buttons = 0;
buttons |= IsButtonBind(gamecontroller, GameControllerButton.A) ? Buttons.A : 0;
buttons |= IsButtonBind(gamecontroller, GameControllerButton.B) ? Buttons.B : 0;
buttons |= IsButtonBind(gamecontroller, GameControllerButton.X) ? Buttons.X : 0;
buttons |= IsButtonBind(gamecontroller, GameControllerButton.Y) ? Buttons.Y : 0;
buttons |= IsButtonBind(gamecontroller, GameControllerButton.START) ? Buttons.Start : 0;
buttons |= IsButtonBind(gamecontroller, GameControllerButton.BACK) ? Buttons.Back : 0;
buttons |= IsButtonBind(gamecontroller, GameControllerButton.LEFTSHOULDER) ? Buttons.LeftShoulder : 0;
buttons |= IsButtonBind(gamecontroller, GameControllerButton.RIGHTSHOULDER) ? Buttons.RightShoulder : 0;
buttons |= IsButtonBind(gamecontroller, GameControllerButton.LEFTSTICK) ? Buttons.LeftStick : 0;
buttons |= IsButtonBind(gamecontroller, GameControllerButton.RIGHTSTICK) ? Buttons.RightStick : 0;
buttons |= IsButtonBind(gamecontroller, GameControllerButton.GUIDE) ? Buttons.BigButton : 0;
buttons |= IsButtonBind(gamecontroller, GameControllerButton.DPAD_DOWN) ? Buttons.DPadDown : 0;
buttons |= IsButtonBind(gamecontroller, GameControllerButton.DPAD_UP) ? Buttons.DPadUp : 0;
buttons |= IsButtonBind(gamecontroller, GameControllerButton.DPAD_LEFT) ? Buttons.DPadLeft : 0;
buttons |= IsButtonBind(gamecontroller, GameControllerButton.DPAD_RIGHT) ? Buttons.DPadRight : 0;
return buttons;
}
bool IsAxisBind(IntPtr gamecontroller, GameControllerAxis axis)
{
GameControllerButtonBind bind =
SDL.GameControllerGetBindForAxis(gamecontroller, axis);
return bind.BindType == GameControllerBindType.Axis;
}
bool IsButtonBind(IntPtr gamecontroller, GameControllerButton button)
{
GameControllerButtonBind bind =
SDL.GameControllerGetBindForButton(gamecontroller, button);
return bind.BindType == GameControllerBindType.Button;
}
GamePadAxes TranslateAxis(GameControllerAxis axis)
{
switch (axis)
{
case GameControllerAxis.LeftX:
return GamePadAxes.LeftX;
case GameControllerAxis.LeftY:
return GamePadAxes.LeftY;
case GameControllerAxis.RightX:
return GamePadAxes.RightX;
case GameControllerAxis.RightY:
return GamePadAxes.RightY;
case GameControllerAxis.TriggerLeft:
return GamePadAxes.LeftTrigger;
case GameControllerAxis.TriggerRight:
return GamePadAxes.RightTrigger;
default:
throw new ArgumentOutOfRangeException(
String.Format("[SDL] Unknown axis {0}", axis));
}
}
Buttons TranslateButton(GameControllerButton button)
{
switch (button)
{
case GameControllerButton.A:
return Buttons.A;
case GameControllerButton.B:
return Buttons.B;
case GameControllerButton.X:
return Buttons.X;
case GameControllerButton.Y:
return Buttons.Y;
case GameControllerButton.LEFTSHOULDER:
return Buttons.LeftShoulder;
case GameControllerButton.RIGHTSHOULDER:
return Buttons.RightShoulder;
case GameControllerButton.LEFTSTICK:
return Buttons.LeftStick;
case GameControllerButton.RIGHTSTICK:
return Buttons.RightStick;
case GameControllerButton.DPAD_UP:
return Buttons.DPadUp;
case GameControllerButton.DPAD_DOWN:
return Buttons.DPadDown;
case GameControllerButton.DPAD_LEFT:
return Buttons.DPadLeft;
case GameControllerButton.DPAD_RIGHT:
return Buttons.DPadRight;
case GameControllerButton.BACK:
return Buttons.Back;
case GameControllerButton.START:
return Buttons.Start;
case GameControllerButton.GUIDE:
return Buttons.BigButton;
default:
Debug.Print("[SDL2] Unknown button {0}", button);
return 0;
}
}
#endif
#endregion
#region Public Members
public void ProcessJoystickEvent(JoyDeviceEvent ev)
{
int id = ev.Which;
if (id < 0)
{
Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type);
return;
}
switch (ev.Type)
{
case EventType.JOYDEVICEADDED:
{
IntPtr handle = SDL.JoystickOpen(id);
if (handle != IntPtr.Zero)
{
int device_id = id;
int instance_id = last_joystick_instance++;
JoystickDevice<Sdl2JoystickDetails> joystick = OpenJoystick(id);
if (joystick != null)
{
joystick.Details.IsConnected = true;
if (device_id < joysticks.Count)
{
joysticks[device_id] = joystick;
}
else
{
joysticks.Add(joystick); joysticks.Add(joystick);
} }
sdl_instanceid_to_joysticks.Add(instance_id, device_id);
} }
} }
}
break;
case EventType.JOYDEVICEREMOVED:
if (IsJoystickInstanceValid(id))
{
int instance_id = id;
int device_id = sdl_instanceid_to_joysticks[instance_id];
JoystickDevice<Sdl2JoystickDetails> joystick = (JoystickDevice<Sdl2JoystickDetails>)joysticks[device_id];
joystick.Details.IsConnected = false;
sdl_instanceid_to_joysticks.Remove(instance_id);
}
else
{
Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type);
}
break;
}
}
public void ProcessJoystickEvent(JoyAxisEvent ev)
{
int id = ev.Which;
if (IsJoystickInstanceValid(id))
{
int index = sdl_instanceid_to_joysticks[id];
JoystickDevice<Sdl2JoystickDetails> joystick = (JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
float value = ev.Value * RangeMultiplier;
joystick.SetAxis((JoystickAxis)ev.Axis, value);
joystick.Details.PacketNumber = Math.Max(0, unchecked(joystick.Details.PacketNumber + 1));
}
else
{
Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type);
}
}
public void ProcessJoystickEvent(JoyBallEvent ev)
{
int id = ev.Which;
if (IsJoystickInstanceValid(id))
{
int index = sdl_instanceid_to_joysticks[id];
JoystickDevice<Sdl2JoystickDetails> joystick = (JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
// Todo: does it make sense to support balls?
joystick.Details.PacketNumber = Math.Max(0, unchecked(joystick.Details.PacketNumber + 1));
}
else
{
Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type);
}
}
public void ProcessJoystickEvent(JoyButtonEvent ev)
{
int id = ev.Which;
if (IsJoystickInstanceValid(id))
{
int index = sdl_instanceid_to_joysticks[id];
JoystickDevice<Sdl2JoystickDetails> joystick = (JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
joystick.SetButton((JoystickButton)ev.Button, ev.State == State.Pressed);
joystick.Details.PacketNumber = Math.Max(0, unchecked(joystick.Details.PacketNumber + 1));
}
else
{
Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type);
}
}
public void ProcessJoystickEvent(JoyHatEvent ev)
{
int id = ev.Which;
if (IsJoystickInstanceValid(id))
{
int index = sdl_instanceid_to_joysticks[id];
JoystickDevice<Sdl2JoystickDetails> joystick = (JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
// Todo: map hat to an extra axis
joystick.Details.PacketNumber = Math.Max(0, unchecked(joystick.Details.PacketNumber + 1));
}
else
{
Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type);
}
}
#if USE_SDL2_GAMECONTROLLER
public void ProcessControllerEvent(ControllerDeviceEvent ev)
{
int id = ev.Which;
if (id < 0)
{
Debug.Print("[SDL2] Invalid controller id {0} in {1}", id, ev.Type);
return;
}
switch (ev.Type)
{
case EventType.CONTROLLERDEVICEADDED:
IntPtr handle = SDL.GameControllerOpen(id);
if (handle != IntPtr.Zero)
{
// The id variable here corresponds to a device_id between 0 and Sdl.NumJoysticks().
// It is only used in the ADDED event. All other events use an instance_id which increases
// monotonically in each ADDED event.
// The idea is that device_id refers to the n-th connected joystick, whereas instance_id
// refers to the actual hardware device behind the n-th joystick.
// Yes, it's confusing.
int device_id = id;
int instance_id = last_controllers_instance++;
Sdl2GamePad pad = new Sdl2GamePad(handle);
IntPtr joystick = SDL.GameControllerGetJoystick(handle);
if (joystick != IntPtr.Zero)
{
pad.Capabilities = new GamePadCapabilities(
GamePadType.GamePad,
GetBoundAxes(joystick),
GetBoundButtons(joystick),
true);
pad.State.SetConnected(true);
// Connect this device and add the relevant device index
if (controllers.Count <= id)
{
controllers.Add(pad);
}
else
{
controllers[device_id] = pad;
}
sdl_instanceid_to_controllers.Add(instance_id, device_id);
}
else
{
Debug.Print("[SDL2] Failed to retrieve joystick from game controller. Error: {0}", SDL.GetError());
}
}
break;
case EventType.CONTROLLERDEVICEREMOVED:
if (IsControllerInstanceValid(id))
{
int instance_id = id;
int device_id = sdl_instanceid_to_controllers[instance_id];
controllers[device_id].State.SetConnected(false);
sdl_instanceid_to_controllers.Remove(device_id);
}
else
{
Debug.Print("[SDL2] Invalid game controller instance {0} in {1}", id, ev.Type);
}
break;
case EventType.CONTROLLERDEVICEREMAPPED:
if (IsControllerInstanceValid(id))
{
// Todo: what should we do in this case?
}
else
{
Debug.Print("[SDL2] Invalid game controller instance {0} in {1}", id, ev.Type);
}
break;
}
}
public void ProcessControllerEvent(ControllerAxisEvent ev)
{
int instance_id = ev.Which;
if (IsControllerInstanceValid(instance_id))
{
int id = sdl_instanceid_to_controllers[instance_id];
controllers[id].State.SetAxis(TranslateAxis(ev.Axis), ev.Value);
}
else
{
Debug.Print("[SDL2] Invalid game controller instance {0} in {1}", instance_id, ev.Type);
}
}
public void ProcessControllerEvent(ControllerButtonEvent ev)
{
int instance_id = ev.Which;
if (IsControllerInstanceValid(instance_id))
{
int id = sdl_instanceid_to_controllers[instance_id];
controllers[id].State.SetButton(TranslateButton(ev.Button), ev.State == State.Pressed);
}
else
{
Debug.Print("[SDL2] Invalid game controller instance {0} in {1}", instance_id, ev.Type);
}
}
#endif
#endregion #endregion
@ -101,49 +522,114 @@ namespace OpenTK.Platform.SDL2
public void Poll() public void Poll()
{ {
SDL.JoystickUpdate(); // Do nothing
foreach (var j in joysticks)
{
var joystick = (JoystickDevice<Sdl2JoystickDetails>)j;
IntPtr handle = joystick.Details.Handle;
for (int i = 0; i < joystick.Axis.Count; i++)
{
var axis = JoystickAxis.Axis0 + i;
joystick.SetAxis(axis, SDL.JoystickGetAxis(handle, i) * joystick.Details.RangeMultiplier);
}
for (int i = 0; i < joystick.Button.Count; i++)
{
var button = JoystickButton.Button0 + i;
joystick.SetButton(button, SDL.JoystickGetButton(handle, i) != 0);
}
}
} }
#endregion #endregion
#region IGamePadDriver Members #region IGamePadDriver Members
public GamePadState GetState() #if USE_SDL2_GAMECONTOLLER
public GamePadCapabilities GetCapabilities(int index)
{ {
return new GamePadState(); if (IsControllerValid(index))
{
return controllers[index].Capabilities;
}
return new GamePadCapabilities();
} }
public GamePadState GetState(int index) public GamePadState GetState(int index)
{ {
if (joysticks.Count >= index) if (IsControllerValid(index))
{ {
return controllers[index].State;
} }
return new GamePadState(); return new GamePadState();
} }
public string GetDeviceName(int index) public string GetName(int index)
{ {
return String.Empty; return String.Empty;
} }
#else
public GamePadCapabilities GetCapabilities(int index)
{
return gamepad_driver.GetCapabilities(index);
}
public GamePadState GetState(int index)
{
return gamepad_driver.GetState(index);
}
public string GetName(int index)
{
return gamepad_driver.GetName(index);
}
public bool SetVibration(int index, float left, float right)
{
return false;
}
#endif
#endregion
#region IJoystickDriver2 Members
JoystickState IJoystickDriver2.GetState(int index)
{
JoystickState state = new JoystickState();
if (IsJoystickValid(index))
{
JoystickDevice<Sdl2JoystickDetails> joystick =
(JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
for (int i = 0; i < joystick.Axis.Count; i++)
{
state.SetAxis(JoystickAxis.Axis0 + i, (short)(joystick.Axis[i] * short.MaxValue + 0.5f));
}
for (int i = 0; i < joystick.Button.Count; i++)
{
state.SetButton(JoystickButton.Button0 + i, joystick.Button[i]);
}
state.SetIsConnected(joystick.Details.IsConnected);
state.SetPacketNumber(joystick.Details.PacketNumber);
}
return state;
}
JoystickCapabilities IJoystickDriver2.GetCapabilities(int index)
{
if (IsJoystickValid(index))
{
JoystickDevice<Sdl2JoystickDetails> joystick =
(JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
return new JoystickCapabilities(
joystick.Axis.Count,
joystick.Button.Count,
joystick.Details.IsConnected);
}
return new JoystickCapabilities();
}
Guid IJoystickDriver2.GetGuid(int index)
{
Guid guid = new Guid();
if (IsJoystickValid(index))
{
JoystickDevice<Sdl2JoystickDetails> joystick =
(JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
return joystick.Details.Guid;
}
return guid;
}
#endregion #endregion

View file

@ -70,7 +70,7 @@ namespace OpenTK.Platform.SDL2
// Argument for KeyDown and KeyUp events (allocated once to avoid runtime allocations) // Argument for KeyDown and KeyUp events (allocated once to avoid runtime allocations)
readonly KeyboardKeyEventArgs key_args = new KeyboardKeyEventArgs(); readonly KeyboardKeyEventArgs key_args = new KeyboardKeyEventArgs();
readonly IInputDriver input_driver = new Sdl2InputDriver(); readonly IInputDriver input_driver;
readonly EventFilter EventFilterDelegate_GCUnsafe = FilterEvents; readonly EventFilter EventFilterDelegate_GCUnsafe = FilterEvents;
readonly IntPtr EventFilterDelegate; readonly IntPtr EventFilterDelegate;
@ -81,10 +81,13 @@ namespace OpenTK.Platform.SDL2
static readonly Sdl2KeyMap map = new Sdl2KeyMap(); static readonly Sdl2KeyMap map = new Sdl2KeyMap();
public Sdl2NativeWindow(int x, int y, int width, int height, public Sdl2NativeWindow(int x, int y, int width, int height,
string title, GameWindowFlags options, DisplayDevice device) string title, GameWindowFlags options, DisplayDevice device,
IInputDriver input_driver)
{ {
lock (sync) lock (sync)
{ {
this.input_driver = input_driver;
var bounds = device.Bounds; var bounds = device.Bounds;
var flags = TranslateFlags(options); var flags = TranslateFlags(options);
flags |= WindowFlags.OPENGL; flags |= WindowFlags.OPENGL;

View file

@ -170,6 +170,32 @@ namespace OpenTK.Platform
#endregion #endregion
#region CreateGetAddress
internal static GraphicsContext.GetAddressDelegate CreateGetAddress()
{
GraphicsContext.GetAddressDelegate loader = null;
if (Configuration.RunningOnWindows)
{
loader = Platform.Windows.Wgl.GetProcAddress;
}
else if (Configuration.RunningOnX11)
{
loader = Platform.X11.Glx.GetProcAddress;
}
else if (Configuration.RunningOnMacOS)
{
loader = Platform.MacOS.NS.GetAddress;
}
else
{
throw new PlatformNotSupportedException();
}
return loader;
}
#endregion
#region --- Creating a Graphics Context --- #region --- Creating a Graphics Context ---
/// <summary> /// <summary>
@ -275,7 +301,7 @@ namespace OpenTK.Platform
public static IWindowInfo CreateSdl2WindowInfo(IntPtr windowHandle) public static IWindowInfo CreateSdl2WindowInfo(IntPtr windowHandle)
{ {
return new OpenTK.Platform.SDL2.Sdl2WindowInfo( return new OpenTK.Platform.SDL2.Sdl2WindowInfo(
SDL2.SDL.CreateWindowFrom(windowHandle), null); windowHandle, null);
} }
#endregion #endregion

View file

@ -151,7 +151,12 @@ namespace OpenTK.Platform.Windows
internal static extern BOOL AdjustWindowRect([In, Out] ref Win32Rectangle lpRect, WindowStyle dwStyle, BOOL bMenu); internal static extern BOOL AdjustWindowRect([In, Out] ref Win32Rectangle lpRect, WindowStyle dwStyle, BOOL bMenu);
[DllImport("user32.dll", EntryPoint = "AdjustWindowRectEx", CallingConvention = CallingConvention.StdCall, SetLastError = true), SuppressUnmanagedCodeSecurity] [DllImport("user32.dll", EntryPoint = "AdjustWindowRectEx", CallingConvention = CallingConvention.StdCall, SetLastError = true), SuppressUnmanagedCodeSecurity]
internal static extern bool AdjustWindowRectEx(ref Win32Rectangle lpRect, WindowStyle dwStyle, bool bMenu, ExtendedWindowStyle dwExStyle); [return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool AdjustWindowRectEx(
ref Win32Rectangle lpRect,
WindowStyle dwStyle,
[MarshalAs(UnmanagedType.Bool)] bool bMenu,
ExtendedWindowStyle dwExStyle);
#endregion #endregion
@ -241,9 +246,7 @@ namespace OpenTK.Platform.Windows
#region CallWindowProc #region CallWindowProc
#if RELEASE
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
#endif
[DllImport("user32.dll", SetLastError = true)] [DllImport("user32.dll", SetLastError = true)]
internal static extern LRESULT CallWindowProc(WNDPROC lpPrevWndFunc, HWND hWnd, WindowMessage Msg, internal static extern LRESULT CallWindowProc(WNDPROC lpPrevWndFunc, HWND hWnd, WindowMessage Msg,
WPARAM wParam, LPARAM lParam); WPARAM wParam, LPARAM lParam);
@ -264,9 +267,9 @@ namespace OpenTK.Platform.Windows
SetLastError(0); SetLastError(0);
if (IntPtr.Size == 4) if (IntPtr.Size == 4)
retval = new IntPtr(SetWindowLong(handle, item, newValue.ToInt32())); retval = new IntPtr(SetWindowLongInternal(handle, item, newValue.ToInt32()));
else else
retval = SetWindowLongPtr(handle, item, newValue); retval = SetWindowLongPtrInternal(handle, item, newValue);
if (retval == IntPtr.Zero) if (retval == IntPtr.Zero)
{ {
@ -283,30 +286,22 @@ namespace OpenTK.Platform.Windows
return SetWindowLong(handle, GetWindowLongOffsets.WNDPROC, Marshal.GetFunctionPointerForDelegate(newValue)); return SetWindowLong(handle, GetWindowLongOffsets.WNDPROC, Marshal.GetFunctionPointerForDelegate(newValue));
} }
#if RELASE
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
#endif [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLong")]
[DllImport("user32.dll", SetLastError = true)] static extern LONG SetWindowLongInternal(HWND hWnd, GetWindowLongOffsets nIndex, LONG dwNewLong);
static extern LONG SetWindowLong(HWND hWnd, GetWindowLongOffsets nIndex, LONG dwNewLong);
#if RELASE
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
#endif [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLongPtr")]
[DllImport("user32.dll", SetLastError = true)] static extern LONG_PTR SetWindowLongPtrInternal(HWND hWnd, GetWindowLongOffsets nIndex, LONG_PTR dwNewLong);
static extern LONG_PTR SetWindowLongPtr(HWND hWnd, GetWindowLongOffsets nIndex, LONG_PTR dwNewLong);
#if RELASE
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
#endif [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLong")]
[DllImport("user32.dll", SetLastError = true)] static extern LONG SetWindowLongInternal(HWND hWnd, GetWindowLongOffsets nIndex,
static extern LONG SetWindowLong(HWND hWnd, GetWindowLongOffsets nIndex,
[MarshalAs(UnmanagedType.FunctionPtr)]WindowProcedure dwNewLong); [MarshalAs(UnmanagedType.FunctionPtr)]WindowProcedure dwNewLong);
#if RELASE
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
#endif [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLongPtr")]
[DllImport("user32.dll", SetLastError = true)] static extern LONG_PTR SetWindowLongPtrInternal(HWND hWnd, GetWindowLongOffsets nIndex,
static extern LONG_PTR SetWindowLongPtr(HWND hWnd, GetWindowLongOffsets nIndex,
[MarshalAs(UnmanagedType.FunctionPtr)]WindowProcedure dwNewLong); [MarshalAs(UnmanagedType.FunctionPtr)]WindowProcedure dwNewLong);
#endregion #endregion
@ -407,9 +402,7 @@ namespace OpenTK.Platform.Windows
#region DispatchMessage #region DispatchMessage
#if RELEASE [SuppressUnmanagedCodeSecurity]
[System.Security.SuppressUnmanagedCodeSecurity]
#endif
[DllImport("User32.dll"), CLSCompliant(false)] [DllImport("User32.dll"), CLSCompliant(false)]
internal static extern LRESULT DispatchMessage(ref MSG msg); internal static extern LRESULT DispatchMessage(ref MSG msg);
@ -417,9 +410,7 @@ namespace OpenTK.Platform.Windows
#region TranslateMessage #region TranslateMessage
#if RELEASE [SuppressUnmanagedCodeSecurity]
[System.Security.SuppressUnmanagedCodeSecurity]
#endif
[DllImport("User32.dll"), CLSCompliant(false)] [DllImport("User32.dll"), CLSCompliant(false)]
internal static extern BOOL TranslateMessage(ref MSG lpMsg); internal static extern BOOL TranslateMessage(ref MSG lpMsg);
@ -3752,7 +3743,7 @@ namespace OpenTK.Platform.Windows
#region WindowMessage #region WindowMessage
internal enum WindowMessage : uint internal enum WindowMessage : int
{ {
NULL = 0x0000, NULL = 0x0000,
CREATE = 0x0001, CREATE = 0x0001,

View file

@ -202,14 +202,14 @@ namespace OpenTK.Platform.Windows
} }
public static public static
Boolean ChoosePixelFormat(IntPtr hdc, int[] piAttribIList, Single[] pfAttribFList, Int32 nMaxFormats, [Out] int[] piFormats, [Out] Int32[] nNumFormats) Boolean ChoosePixelFormat(IntPtr hdc, int[] piAttribIList, Single[] pfAttribFList, Int32 nMaxFormats, [Out] int[] piFormats, out int nNumFormats)
{ {
unsafe unsafe
{ {
fixed (int* piAttribIList_ptr = piAttribIList) fixed (int* piAttribIList_ptr = piAttribIList)
fixed (Single* pfAttribFList_ptr = pfAttribFList) fixed (Single* pfAttribFList_ptr = pfAttribFList)
fixed (int* piFormats_ptr = piFormats) fixed (int* piFormats_ptr = piFormats)
fixed (Int32* nNumFormats_ptr = nNumFormats) fixed (Int32* nNumFormats_ptr = &nNumFormats)
{ {
return Delegates.wglChoosePixelFormatARB((IntPtr)hdc, (int*)piAttribIList_ptr, (Single*)pfAttribFList_ptr, (UInt32)nMaxFormats, (int*)piFormats_ptr, (UInt32*)nNumFormats_ptr); return Delegates.wglChoosePixelFormatARB((IntPtr)hdc, (int*)piAttribIList_ptr, (Single*)pfAttribFList_ptr, (UInt32)nMaxFormats, (int*)piFormats_ptr, (UInt32*)nNumFormats_ptr);
} }

View file

@ -8,6 +8,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Reflection; using System.Reflection;
@ -34,6 +35,9 @@ namespace OpenTK.Platform.Windows
internal const string Library = "OPENGL32.DLL"; internal const string Library = "OPENGL32.DLL";
readonly static Dictionary<string, bool> extensions =
new Dictionary<string, bool>();
private static Assembly assembly; private static Assembly assembly;
private static Type wglClass; private static Type wglClass;
private static Type delegatesClass; private static Type delegatesClass;
@ -121,43 +125,61 @@ namespace OpenTK.Platform.Windows
#endregion #endregion
#region public static partial class Arb public static bool SupportsExtension(string name)
/// <summary>Contains ARB extensions for WGL.</summary>
public static partial class Arb
{ {
return SupportsExtension(Wgl.GetCurrentDC(), name);
}
/// <summary> /// <summary>
/// Checks if a Wgl extension is supported by the given context. /// Checks if a Wgl extension is supported by the given context.
/// </summary> /// </summary>
/// <param name="context">The device context.</param> /// <param name="context">The device context.</param>
/// <param name="ext">The extension to check.</param> /// <param name="ext">The extension to check.</param>
/// <returns>True if the extension is supported by the given context, false otherwise</returns> /// <returns>True if the extension is supported by the given context, false otherwise</returns>
public static bool SupportsExtension(WinGLContext context, string ext) public static bool SupportsExtension(IntPtr dc, string name)
{
if (extensions.Count == 0)
{ {
// We cache this locally, as another thread might create a context which doesn't support this method. // We cache this locally, as another thread might create a context which doesn't support this method.
// The design is far from ideal, but there's no good solution to this issue as long as we are using // The design is far from ideal, but there's no good solution to this issue as long as we are using
// static WGL/GL classes. Fortunately, this issue is extremely unlikely to arise in practice, as you'd // static WGL/GL classes. Fortunately, this issue is extremely unlikely to arise in practice, as you'd
// have to create one accelerated and one non-accelerated context in the same application, with the // have to create one accelerated and one non-accelerated context in the same application, with the
// non-accelerated context coming second. // non-accelerated context coming second.
Wgl.Delegates.GetExtensionsStringARB get = Wgl.Delegates.wglGetExtensionsStringARB; Wgl.Delegates.GetExtensionsStringARB get_arb = Wgl.Delegates.wglGetExtensionsStringARB;
Wgl.Delegates.GetExtensionsStringEXT get_ext = Wgl.Delegates.wglGetExtensionsStringEXT;
IntPtr str_ptr =
get_arb != null ? get_arb(dc) :
get_ext != null ? get_ext() :
IntPtr.Zero;
if (get != null) if (str_ptr != IntPtr.Zero)
{ {
string[] extensions = null; string str;
unsafe unsafe
{ {
extensions = new string((sbyte*)get(context.DeviceContext)) str = new string((sbyte*)str_ptr);
.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
} }
if (extensions == null || extensions.Length == 0)
return false;
foreach (string s in extensions) foreach (string ext in str.Split(' '))
if (s == ext) {
return true; extensions.Add(ext, true);
}
}
}
if (extensions.Count > 0)
{
return extensions.ContainsKey(name);
} }
return false; return false;
} }
#region public static partial class Arb
/// <summary>Contains ARB extensions for WGL.</summary>
public static partial class Arb
{
} }
#endregion #endregion
@ -167,27 +189,6 @@ namespace OpenTK.Platform.Windows
/// <summary>Contains EXT extensions for WGL.</summary> /// <summary>Contains EXT extensions for WGL.</summary>
public static partial class Ext public static partial class Ext
{ {
private static string[] extensions;
/// <summary>
/// Checks if a Wgl extension is supported by the given context.
/// </summary>
/// <param name="ext">The extension to check.</param>
/// <returns>True if the extension is supported by the given context, false otherwise</returns>
public static bool SupportsExtension(string ext)
{
if (Wgl.Delegates.wglGetExtensionsStringEXT != null)
{
if (extensions == null || rebuildExtensionList)
{
extensions = Wgl.Ext.GetExtensionsString().Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
Array.Sort(extensions);
rebuildExtensionList = false;
}
return Array.BinarySearch(extensions, ext) != -1;
}
return false;
}
} }
#endregion #endregion

View file

@ -131,6 +131,11 @@ namespace OpenTK.Platform.Windows
return InputDriver.GamePadDriver; return InputDriver.GamePadDriver;
} }
public IJoystickDriver2 CreateJoystickDriver()
{
return InputDriver.JoystickDriver;
}
#endregion #endregion
IInputDriver2 InputDriver IInputDriver2 InputDriver

View file

@ -31,6 +31,7 @@ namespace OpenTK.Platform.Windows
IntPtr device_context; IntPtr device_context;
bool vsync_supported; bool vsync_supported;
bool vsync_tear_supported;
readonly WinGraphicsMode ModeSelector; readonly WinGraphicsMode ModeSelector;
@ -279,14 +280,14 @@ namespace OpenTK.Platform.Windows
throw new ArgumentException("window", "Must point to a valid window."); throw new ArgumentException("window", "Must point to a valid window.");
success = Wgl.MakeCurrent(wnd.DeviceContext, Handle.Handle); success = Wgl.MakeCurrent(wnd.DeviceContext, Handle.Handle);
device_context = wnd.DeviceContext;
} }
else else
{ {
success = Wgl.MakeCurrent(IntPtr.Zero, IntPtr.Zero); success = Wgl.MakeCurrent(IntPtr.Zero, IntPtr.Zero);
device_context = IntPtr.Zero;
} }
device_context = wnd.DeviceContext;
if (!success) if (!success)
{ {
throw new GraphicsContextException(String.Format( throw new GraphicsContextException(String.Format(
@ -325,10 +326,16 @@ namespace OpenTK.Platform.Windows
lock (LoadLock) lock (LoadLock)
{ {
if (vsync_supported) if (vsync_supported)
{
if (value < 0 && !vsync_tear_supported)
{
value = 1;
}
Wgl.Ext.SwapInterval(value); Wgl.Ext.SwapInterval(value);
} }
} }
} }
}
#endregion #endregion
@ -339,8 +346,11 @@ namespace OpenTK.Platform.Windows
lock (LoadLock) lock (LoadLock)
{ {
Wgl.LoadAll(); Wgl.LoadAll();
vsync_supported = Wgl.Arb.SupportsExtension(this, "WGL_EXT_swap_control") && vsync_supported =
Wgl.SupportsExtension(DeviceContext, "WGL_EXT_swap_control") &&
Wgl.Load("wglGetSwapIntervalEXT") && Wgl.Load("wglSwapIntervalEXT"); Wgl.Load("wglGetSwapIntervalEXT") && Wgl.Load("wglSwapIntervalEXT");
vsync_tear_supported =
Wgl.SupportsExtension(DeviceContext, "WGL_EXT_swap_tear");
} }
base.LoadAll(); base.LoadAll();

View file

@ -240,15 +240,10 @@ namespace OpenTK.Platform.Windows
#endregion #endregion
#region WindowProcedure #region Message Handlers
IntPtr WindowProcedure(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) void HandleActivate(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{ {
switch (message)
{
#region Size / Move / Style events
case WindowMessage.ACTIVATE:
// See http://msdn.microsoft.com/en-us/library/ms646274(VS.85).aspx (WM_ACTIVATE notification): // See http://msdn.microsoft.com/en-us/library/ms646274(VS.85).aspx (WM_ACTIVATE notification):
// wParam: The low-order word specifies whether the window is being activated or deactivated. // wParam: The low-order word specifies whether the window is being activated or deactivated.
bool new_focused_state = Focused; bool new_focused_state = Focused;
@ -259,10 +254,10 @@ namespace OpenTK.Platform.Windows
if (new_focused_state != Focused) if (new_focused_state != Focused)
FocusedChanged(this, EventArgs.Empty); FocusedChanged(this, EventArgs.Empty);
break; }
case WindowMessage.ENTERMENULOOP: void HandleEnterModalLoop(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
case WindowMessage.ENTERSIZEMOVE: {
// Entering the modal size/move loop: we don't want rendering to // Entering the modal size/move loop: we don't want rendering to
// stop during this time, so we register a timer callback to continue // stop during this time, so we register a timer callback to continue
// processing from time to time. // processing from time to time.
@ -271,10 +266,10 @@ namespace OpenTK.Platform.Windows
if (!CursorVisible) if (!CursorVisible)
UngrabCursor(); UngrabCursor();
break; }
case WindowMessage.EXITMENULOOP: void HandleExitModalLoop(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
case WindowMessage.EXITSIZEMOVE: {
// Exiting from Modal size/move loop: the timer callback is no longer // Exiting from Modal size/move loop: the timer callback is no longer
// necessary. // necessary.
is_in_modal_loop = false; is_in_modal_loop = false;
@ -283,12 +278,10 @@ namespace OpenTK.Platform.Windows
// Ensure cursor remains grabbed // Ensure cursor remains grabbed
if (!CursorVisible) if (!CursorVisible)
GrabCursor(); GrabCursor();
break; }
case WindowMessage.ERASEBKGND: void HandleWindowPositionChanged(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
return new IntPtr(1); {
case WindowMessage.WINDOWPOSCHANGED:
unsafe unsafe
{ {
WindowPosition* pos = (WindowPosition*)lParam; WindowPosition* pos = (WindowPosition*)lParam;
@ -329,38 +322,56 @@ namespace OpenTK.Platform.Windows
} }
} }
} }
break; }
void HandleStyleChanged(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
WindowBorder old_border = windowBorder;
WindowBorder new_border = old_border;
case WindowMessage.STYLECHANGED:
unsafe unsafe
{ {
if (wParam.ToInt64() == (long)GWL.STYLE) GWL get_window_style = (GWL)unchecked(wParam.ToInt32());
if ((get_window_style & (GWL.STYLE | GWL.EXSTYLE)) != 0)
{ {
WindowStyle style = ((StyleStruct*)lParam)->New; WindowStyle style = ((StyleStruct*)lParam)->New;
if ((style & WindowStyle.Popup) != 0) if ((style & WindowStyle.Popup) != 0)
windowBorder = WindowBorder.Hidden; new_border = WindowBorder.Hidden;
else if ((style & WindowStyle.ThickFrame) != 0) else if ((style & WindowStyle.ThickFrame) != 0)
windowBorder = WindowBorder.Resizable; new_border = WindowBorder.Resizable;
else if ((style & ~(WindowStyle.ThickFrame | WindowStyle.MaximizeBox)) != 0) else if ((style & ~(WindowStyle.ThickFrame | WindowStyle.MaximizeBox)) != 0)
windowBorder = WindowBorder.Fixed; new_border = WindowBorder.Fixed;
} }
} }
if (new_border != windowBorder)
{
// Ensure cursor remains grabbed // Ensure cursor remains grabbed
if (!CursorVisible) if (!CursorVisible)
GrabCursor(); GrabCursor();
break; windowBorder = new_border;
WindowBorderChanged(this, EventArgs.Empty);
}
}
case WindowMessage.SIZE: void HandleSize(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
SizeMessage state = (SizeMessage)wParam.ToInt64(); SizeMessage state = (SizeMessage)wParam.ToInt64();
WindowState new_state = windowState; WindowState new_state = windowState;
switch (state) switch (state)
{ {
case SizeMessage.RESTORED: new_state = borderless_maximized_window_state ? case SizeMessage.RESTORED:
WindowState.Maximized : WindowState.Normal; break; new_state = borderless_maximized_window_state ?
case SizeMessage.MINIMIZED: new_state = WindowState.Minimized; break; WindowState.Maximized : WindowState.Normal;
case SizeMessage.MAXIMIZED: new_state = WindowBorder == WindowBorder.Hidden ? break;
case SizeMessage.MINIMIZED:
new_state = WindowState.Minimized;
break;
case SizeMessage.MAXIMIZED:
new_state = WindowBorder == WindowBorder.Hidden ?
WindowState.Fullscreen : WindowState.Maximized; WindowState.Fullscreen : WindowState.Maximized;
break; break;
} }
@ -374,23 +385,20 @@ namespace OpenTK.Platform.Windows
if (!CursorVisible) if (!CursorVisible)
GrabCursor(); GrabCursor();
} }
}
break; void HandleChar(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
#endregion
#region Input events
case WindowMessage.CHAR:
if (IntPtr.Size == 4) if (IntPtr.Size == 4)
key_press.KeyChar = (char)wParam.ToInt32(); key_press.KeyChar = (char)wParam.ToInt32();
else else
key_press.KeyChar = (char)wParam.ToInt64(); key_press.KeyChar = (char)wParam.ToInt64();
KeyPress(this, key_press); KeyPress(this, key_press);
break; }
case WindowMessage.MOUSEMOVE: void HandleMouseMove(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Point point = new Point( Point point = new Point(
(short)((uint)lParam.ToInt32() & 0x0000FFFF), (short)((uint)lParam.ToInt32() & 0x0000FFFF),
(short)(((uint)lParam.ToInt32() & 0xFFFF0000) >> 16)); (short)(((uint)lParam.ToInt32() & 0xFFFF0000) >> 16));
@ -405,68 +413,75 @@ namespace OpenTK.Platform.Windows
MouseEnter(this, EventArgs.Empty); MouseEnter(this, EventArgs.Empty);
} }
break; }
case WindowMessage.MOUSELEAVE: void HandleMouseLeave(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
mouse_outside_window = true; mouse_outside_window = true;
// Mouse tracking is disabled automatically by the OS // Mouse tracking is disabled automatically by the OS
MouseLeave(this, EventArgs.Empty); MouseLeave(this, EventArgs.Empty);
break; }
case WindowMessage.MOUSEWHEEL: void HandleMouseWheel(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
// This is due to inconsistent behavior of the WParam value on 64bit arch, whese // This is due to inconsistent behavior of the WParam value on 64bit arch, whese
// wparam = 0xffffffffff880000 or wparam = 0x00000000ff100000 // wparam = 0xffffffffff880000 or wparam = 0x00000000ff100000
mouse.WheelPrecise += ((long)wParam << 32 >> 48) / 120.0f; mouse.WheelPrecise += ((long)wParam << 32 >> 48) / 120.0f;
break; }
case WindowMessage.LBUTTONDOWN: void HandleLButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.SetCapture(window.Handle); Functions.SetCapture(window.Handle);
mouse[MouseButton.Left] = true; mouse[MouseButton.Left] = true;
break; }
case WindowMessage.MBUTTONDOWN: void HandleMButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.SetCapture(window.Handle); Functions.SetCapture(window.Handle);
mouse[MouseButton.Middle] = true; mouse[MouseButton.Middle] = true;
break; }
case WindowMessage.RBUTTONDOWN: void HandleRButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.SetCapture(window.Handle); Functions.SetCapture(window.Handle);
mouse[MouseButton.Right] = true; mouse[MouseButton.Right] = true;
break; }
case WindowMessage.XBUTTONDOWN: void HandleXButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.SetCapture(window.Handle); Functions.SetCapture(window.Handle);
mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) != mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) !=
(int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = true; (int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = true;
break; }
case WindowMessage.LBUTTONUP: void HandleLButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.ReleaseCapture(); Functions.ReleaseCapture();
mouse[MouseButton.Left] = false; mouse[MouseButton.Left] = false;
break; }
case WindowMessage.MBUTTONUP: void HandleMButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.ReleaseCapture(); Functions.ReleaseCapture();
mouse[MouseButton.Middle] = false; mouse[MouseButton.Middle] = false;
break; }
case WindowMessage.RBUTTONUP: void HandleRButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.ReleaseCapture(); Functions.ReleaseCapture();
mouse[MouseButton.Right] = false; mouse[MouseButton.Right] = false;
break; }
case WindowMessage.XBUTTONUP: void HandleXButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.ReleaseCapture(); Functions.ReleaseCapture();
mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) != mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) !=
(int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = false; (int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = false;
break; }
// Keyboard events: void HandleKeyboard(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
case WindowMessage.KEYDOWN: {
case WindowMessage.KEYUP:
case WindowMessage.SYSKEYDOWN:
case WindowMessage.SYSKEYUP:
bool pressed = bool pressed =
message == WindowMessage.KEYDOWN || message == WindowMessage.KEYDOWN ||
message == WindowMessage.SYSKEYDOWN; message == WindowMessage.SYSKEYDOWN;
@ -498,23 +513,16 @@ namespace OpenTK.Platform.Windows
key_up.Key = key; key_up.Key = key;
KeyUp(this, key_up); KeyUp(this, key_up);
} }
}
} }
return IntPtr.Zero; void HandleKillFocus(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
case WindowMessage.SYSCHAR:
return IntPtr.Zero;
case WindowMessage.KILLFOCUS:
keyboard.ClearKeys(); keyboard.ClearKeys();
break; }
#endregion void HandleCreate(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
#region Creation / Destruction events
case WindowMessage.CREATE:
CreateStruct cs = (CreateStruct)Marshal.PtrToStructure(lParam, typeof(CreateStruct)); CreateStruct cs = (CreateStruct)Marshal.PtrToStructure(lParam, typeof(CreateStruct));
if (cs.hwndParent == IntPtr.Zero) if (cs.hwndParent == IntPtr.Zero)
{ {
@ -529,9 +537,10 @@ namespace OpenTK.Platform.Windows
invisible_since_creation = true; invisible_since_creation = true;
} }
break; }
case WindowMessage.CLOSE: void HandleClose(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
System.ComponentModel.CancelEventArgs e = new System.ComponentModel.CancelEventArgs(); System.ComponentModel.CancelEventArgs e = new System.ComponentModel.CancelEventArgs();
Closing(this, e); Closing(this, e);
@ -539,20 +548,143 @@ namespace OpenTK.Platform.Windows
if (!e.Cancel) if (!e.Cancel)
{ {
DestroyWindow(); DestroyWindow();
break; }
} }
return IntPtr.Zero; void HandleDestroy(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
case WindowMessage.DESTROY:
exists = false; exists = false;
if (handle == window.Handle)
{
Functions.UnregisterClass(ClassName, Instance); Functions.UnregisterClass(ClassName, Instance);
}
window.Dispose(); window.Dispose();
child_window.Dispose(); child_window.Dispose();
Closed(this, EventArgs.Empty); Closed(this, EventArgs.Empty);
}
#endregion
#region WindowProcedure
IntPtr WindowProcedure(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
switch (message)
{
#region Size / Move / Style events
case WindowMessage.ACTIVATE:
HandleActivate(handle, message, wParam, lParam);
break;
case WindowMessage.ENTERMENULOOP:
case WindowMessage.ENTERSIZEMOVE:
HandleEnterModalLoop(handle, message, wParam, lParam);
break;
case WindowMessage.EXITMENULOOP:
case WindowMessage.EXITSIZEMOVE:
HandleExitModalLoop(handle, message, wParam, lParam);
break;
case WindowMessage.ERASEBKGND:
return new IntPtr(1);
case WindowMessage.WINDOWPOSCHANGED:
HandleWindowPositionChanged(handle, message, wParam, lParam);
break;
case WindowMessage.STYLECHANGED:
HandleStyleChanged(handle, message, wParam, lParam);
break;
case WindowMessage.SIZE:
HandleSize(handle, message, wParam, lParam);
break;
#endregion
#region Input events
case WindowMessage.CHAR:
HandleChar(handle, message, wParam, lParam);
break;
case WindowMessage.MOUSEMOVE:
HandleMouseMove(handle, message, wParam, lParam);
break;
case WindowMessage.MOUSELEAVE:
HandleMouseLeave(handle, message, wParam, lParam);
break;
case WindowMessage.MOUSEWHEEL:
HandleMouseWheel(handle, message, wParam, lParam);
break;
case WindowMessage.LBUTTONDOWN:
HandleLButtonDown(handle, message, wParam, lParam);
break;
case WindowMessage.MBUTTONDOWN:
HandleMButtonDown(handle, message, wParam, lParam);
break;
case WindowMessage.RBUTTONDOWN:
HandleRButtonDown(handle, message, wParam, lParam);
break;
case WindowMessage.XBUTTONDOWN:
HandleXButtonDown(handle, message, wParam, lParam);
break;
case WindowMessage.LBUTTONUP:
HandleLButtonUp(handle, message, wParam, lParam);
break;
case WindowMessage.MBUTTONUP:
HandleMButtonUp(handle, message, wParam, lParam);
break;
case WindowMessage.RBUTTONUP:
HandleRButtonUp(handle, message, wParam, lParam);
break;
case WindowMessage.XBUTTONUP:
HandleXButtonUp(handle, message, wParam, lParam);
break;
// Keyboard events:
case WindowMessage.KEYDOWN:
case WindowMessage.KEYUP:
case WindowMessage.SYSKEYDOWN:
case WindowMessage.SYSKEYUP:
HandleKeyboard(handle, message, wParam, lParam);
return IntPtr.Zero;
case WindowMessage.SYSCHAR:
return IntPtr.Zero;
case WindowMessage.KILLFOCUS:
HandleKillFocus(handle, message, wParam, lParam);
break;
#endregion
#region Creation / Destruction events
case WindowMessage.CREATE:
HandleCreate(handle, message, wParam, lParam);
break;
case WindowMessage.CLOSE:
HandleClose(handle, message, wParam, lParam);
return IntPtr.Zero;
case WindowMessage.DESTROY:
HandleDestroy(handle, message, wParam, lParam);
break; break;
#endregion #endregion
@ -1117,34 +1249,35 @@ namespace OpenTK.Platform.Windows
WindowState state = WindowState; WindowState state = WindowState;
ResetWindowState(); ResetWindowState();
WindowStyle style = WindowStyle.ClipChildren | WindowStyle.ClipSiblings; WindowStyle old_style = WindowStyle.ClipChildren | WindowStyle.ClipSiblings;
WindowStyle new_style = old_style;
switch (value) switch (value)
{ {
case WindowBorder.Resizable: case WindowBorder.Resizable:
style |= WindowStyle.OverlappedWindow; new_style |= WindowStyle.OverlappedWindow;
break; break;
case WindowBorder.Fixed: case WindowBorder.Fixed:
style |= WindowStyle.OverlappedWindow & new_style |= WindowStyle.OverlappedWindow &
~(WindowStyle.ThickFrame | WindowStyle.MaximizeBox | WindowStyle.SizeBox); ~(WindowStyle.ThickFrame | WindowStyle.MaximizeBox | WindowStyle.SizeBox);
break; break;
case WindowBorder.Hidden: case WindowBorder.Hidden:
style |= WindowStyle.Popup; new_style |= WindowStyle.Popup;
break; break;
} }
// Make sure client size doesn't change when changing the border style. // Make sure client size doesn't change when changing the border style.
Size client_size = ClientSize; Size client_size = ClientSize;
Win32Rectangle rect = Win32Rectangle.From(client_size); Win32Rectangle rect = Win32Rectangle.From(client_size);
Functions.AdjustWindowRectEx(ref rect, style, false, ParentStyleEx); Functions.AdjustWindowRectEx(ref rect, new_style, false, ParentStyleEx);
// This avoids leaving garbage on the background window. // This avoids leaving garbage on the background window.
if (was_visible) if (was_visible)
Visible = false; Visible = false;
Functions.SetWindowLong(window.Handle, GetWindowLongOffsets.STYLE, (IntPtr)(int)style); Functions.SetWindowLong(window.Handle, GetWindowLongOffsets.STYLE, (IntPtr)(int)new_style);
Functions.SetWindowPos(window.Handle, IntPtr.Zero, 0, 0, rect.Width, rect.Height, Functions.SetWindowPos(window.Handle, IntPtr.Zero, 0, 0, rect.Width, rect.Height,
SetWindowPosFlags.NOMOVE | SetWindowPosFlags.NOZORDER | SetWindowPosFlags.NOMOVE | SetWindowPosFlags.NOZORDER |
SetWindowPosFlags.FRAMECHANGED); SetWindowPosFlags.FRAMECHANGED);
@ -1157,7 +1290,23 @@ namespace OpenTK.Platform.Windows
WindowState = state; WindowState = state;
WindowBorderChanged(this, EventArgs.Empty); // Workaround for github issues #33 and #34,
// where WindowMessage.STYLECHANGED is not
// delivered when running on Mono/Windows.
if (Configuration.RunningOnMono)
{
StyleStruct style = new StyleStruct();
style.New = new_style;
style.Old = old_style;
unsafe
{
HandleStyleChanged(
window.Handle,
WindowMessage.STYLECHANGED,
new IntPtr((int)(GWL.STYLE | GWL.EXSTYLE)),
new IntPtr(&style));
}
}
} }
} }

View file

@ -37,27 +37,28 @@ namespace OpenTK.Platform.Windows
{ {
class WinGraphicsMode : IGraphicsMode class WinGraphicsMode : IGraphicsMode
{ {
#region Fields enum AccelerationType
{
// Software acceleration
None = 0,
// Partial acceleration (Direct3D emulation)
MCD,
// Full acceleration
ICD,
}
readonly List<GraphicsMode> modes = new List<GraphicsMode>();
static readonly object SyncRoot = new object(); static readonly object SyncRoot = new object();
readonly IntPtr Device;
#endregion readonly List<GraphicsMode> modes = new List<GraphicsMode>();
#region Constructors #region Constructors
public WinGraphicsMode(IntPtr device) public WinGraphicsMode(IntPtr device)
{ {
lock (SyncRoot) if (device == IntPtr.Zero)
{ throw new ArgumentException();
modes.AddRange(GetModesARB(device));
if (modes.Count == 0) Device = device;
modes.AddRange(GetModesPFD(device));
if (modes.Count == 0)
throw new GraphicsModeException(
"No GraphicsMode available. This should never happen, please report a bug at http://www.opentk.com");
modes.Sort(new GraphicsModeComparer());
}
} }
#endregion #endregion
@ -67,113 +68,298 @@ namespace OpenTK.Platform.Windows
public GraphicsMode SelectGraphicsMode(ColorFormat color, int depth, int stencil, int samples, public GraphicsMode SelectGraphicsMode(ColorFormat color, int depth, int stencil, int samples,
ColorFormat accum, int buffers, bool stereo) ColorFormat accum, int buffers, bool stereo)
{ {
GraphicsMode mode = null; GraphicsMode mode = new GraphicsMode(color, depth, stencil, samples,accum, buffers, stereo);
do GraphicsMode created_mode = ChoosePixelFormatARB(Device, mode);
{
mode = modes.Find(delegate(GraphicsMode current)
{
return ModeSelector(current, color, depth, stencil, samples, accum, buffers, stereo);
});
} while (mode == null && RelaxParameters(
ref color, ref depth, ref stencil, ref samples, ref accum, ref buffers, ref stereo));
if (mode == null) // If ChoosePixelFormatARB failed, iterate through all acceleration types in turn (ICD, MCD, None)
mode = modes[0]; // This should fix issue #2224, which causes OpenTK to fail on VMs without hardware acceleration.
created_mode = created_mode ?? ChoosePixelFormatPFD(Device, mode, AccelerationType.ICD);
created_mode = created_mode ?? ChoosePixelFormatPFD(Device, mode, AccelerationType.MCD);
created_mode = created_mode ?? ChoosePixelFormatPFD(Device, mode, AccelerationType.None);
return mode; if (created_mode == null)
{
throw new GraphicsModeException("The requested GraphicsMode is not supported");
} }
bool RelaxParameters(ref ColorFormat color, ref int depth, ref int stencil, ref int samples, return created_mode;
ref ColorFormat accum, ref int buffers, ref bool stereo)
{
if (stereo) { stereo = false; return true; }
if (buffers != 2) { buffers = 2; return true; }
if (accum != 0) { accum = 0; return true; }
if (samples != 0) { samples = 0; return true; }
if (depth < 16) { depth = 16; return true; }
if (depth != 24) { depth = 24; return true; }
if (stencil > 0 && stencil != 8) { stencil = 8; return true; }
if (stencil == 8) { stencil = 0; return true; }
if (color < 8) { color = 8; return true; }
if (color < 16) { color = 16; return true; }
if (color < 24) { color = 24; return true; }
if (color < 32 || color > 32) { color = 32; return true; }
return false; // We tried everything we could, no match found.
} }
#endregion #endregion
#region Private Methods #region Private Methods
#region GetModesPFD #region ChoosePixelFormatARB
IEnumerable<GraphicsMode> GetModesPFD(IntPtr device)
{
Debug.WriteLine(String.Format("Device context: {0}", device));
Debug.WriteLine("Retrieving PFD pixel formats... ");
PixelFormatDescriptor pfd = new PixelFormatDescriptor();
pfd.Size = API.PixelFormatDescriptorSize;
pfd.Version = API.PixelFormatDescriptorVersion;
pfd.Flags =
PixelFormatDescriptorFlags.SUPPORT_OPENGL |
PixelFormatDescriptorFlags.DRAW_TO_WINDOW;
// Make sure we don't turn off Aero on Vista and newer.
if (Environment.OSVersion.Version.Major >= 6)
{
pfd.Flags |= PixelFormatDescriptorFlags.SUPPORT_COMPOSITION;
}
foreach (bool generic_allowed in new bool[] { false, true })
{
// Iterate through all accelerated formats first. Afterwards, iterate through non-accelerated formats.
// This should fix issue #2224, which causes OpenTK to fail on VMs without hardware acceleration.
// Note: DescribePixelFormat found in gdi32 is extremely slow on nvidia, for some reason.
int pixel = 0;
while (Functions.DescribePixelFormat(device, ++pixel, API.PixelFormatDescriptorSize, ref pfd) != 0)
{
// Ignore non-accelerated formats.
if (!generic_allowed && (pfd.Flags & PixelFormatDescriptorFlags.GENERIC_FORMAT) != 0)
continue;
GraphicsMode fmt = new GraphicsMode((IntPtr)pixel,
new ColorFormat(pfd.RedBits, pfd.GreenBits, pfd.BlueBits, pfd.AlphaBits),
pfd.DepthBits,
pfd.StencilBits,
0,
new ColorFormat(pfd.AccumBits),
(pfd.Flags & PixelFormatDescriptorFlags.DOUBLEBUFFER) != 0 ? 2 : 1,
(pfd.Flags & PixelFormatDescriptorFlags.STEREO) != 0);
yield return fmt;
}
}
}
#endregion
#region GetModesARB
// Queries pixel formats through the WGL_ARB_pixel_format extension // Queries pixel formats through the WGL_ARB_pixel_format extension
// This method only returns accelerated formats. If no format offers // This method only returns accelerated formats. If no format offers
// hardware acceleration (e.g. we are running in a VM or in a remote desktop // hardware acceleration (e.g. we are running in a VM or in a remote desktop
// connection), this method will return 0 formats and we will fall back to // connection), this method will return 0 formats and we will fall back to
// GetModesPFD. // ChoosePixelFormatPFD.
IEnumerable<GraphicsMode> GetModesARB(IntPtr device) GraphicsMode ChoosePixelFormatARB(IntPtr device, GraphicsMode mode)
{ {
// See http://www.opengl.org/registry/specs/ARB/wgl_pixel_format.txt GraphicsMode created_mode = null;
// for more details if (Wgl.SupportsExtension("WGL_ARB_pixel_format") &&
Debug.Write("Retrieving ARB pixel formats.... "); Wgl.Delegates.wglChoosePixelFormatARB != null)
if (Wgl.Delegates.wglChoosePixelFormatARB == null || Wgl.Delegates.wglGetPixelFormatAttribivARB == null)
{ {
Debug.WriteLine("failed."); List<int> attributes = new List<int>();
yield break; attributes.Add((int)WGL_ARB_pixel_format.AccelerationArb);
attributes.Add((int)WGL_ARB_pixel_format.FullAccelerationArb);
attributes.Add((int)WGL_ARB_pixel_format.DrawToWindowArb);
attributes.Add(1);
if (mode.ColorFormat.Red > 0)
{
attributes.Add((int)WGL_ARB_pixel_format.RedBitsArb);
attributes.Add(mode.ColorFormat.Red);
} }
if (mode.ColorFormat.Green > 0)
{
attributes.Add((int)WGL_ARB_pixel_format.GreenBitsArb);
attributes.Add(mode.ColorFormat.Green);
}
if (mode.ColorFormat.Blue > 0)
{
attributes.Add((int)WGL_ARB_pixel_format.BlueBitsArb);
attributes.Add(mode.ColorFormat.Blue);
}
if (mode.ColorFormat.Alpha > 0)
{
attributes.Add((int)WGL_ARB_pixel_format.AlphaBitsArb);
attributes.Add(mode.ColorFormat.Alpha);
}
if (mode.Depth > 0)
{
attributes.Add((int)WGL_ARB_pixel_format.DepthBitsArb);
attributes.Add(mode.Depth);
}
if (mode.Stencil > 0)
{
attributes.Add((int)WGL_ARB_pixel_format.StencilBitsArb);
attributes.Add(mode.Stencil);
}
if (mode.AccumulatorFormat.Red > 0)
{
attributes.Add((int)WGL_ARB_pixel_format.AccumRedBitsArb);
attributes.Add(mode.AccumulatorFormat.Red);
}
if (mode.AccumulatorFormat.Green > 0)
{
attributes.Add((int)WGL_ARB_pixel_format.AccumGreenBitsArb);
attributes.Add(mode.AccumulatorFormat.Green);
}
if (mode.AccumulatorFormat.Blue > 0)
{
attributes.Add((int)WGL_ARB_pixel_format.AccumBlueBitsArb);
attributes.Add(mode.AccumulatorFormat.Blue);
}
if (mode.AccumulatorFormat.Alpha > 0)
{
attributes.Add((int)WGL_ARB_pixel_format.AccumAlphaBitsArb);
attributes.Add(mode.AccumulatorFormat.Alpha);
}
if (mode.Samples > 0 &&
Wgl.SupportsExtension("WGL_ARB_multisample"))
{
attributes.Add((int)WGL_ARB_multisample.SampleBuffersArb);
attributes.Add(1);
attributes.Add((int)WGL_ARB_multisample.SamplesArb);
attributes.Add(mode.Samples);
}
if (mode.Buffers > 0)
{
attributes.Add((int)WGL_ARB_pixel_format.DoubleBufferArb);
attributes.Add(mode.Buffers > 1 ? 1 : 0);
}
if (mode.Stereo)
{
attributes.Add((int)WGL_ARB_pixel_format.StereoArb);
attributes.Add(1);
}
attributes.Add(0);
attributes.Add(0);
int[] format = new int[1];
int count;
if (Wgl.Arb.ChoosePixelFormat(device, attributes.ToArray(), null, format.Length, format, out count)
&& count > 0)
{
created_mode = DescribePixelFormatARB(device, format[0]);
}
else
{
Debug.Print("[WGL] ChoosePixelFormatARB failed with {0}", Marshal.GetLastWin32Error());
}
}
else
{
Debug.WriteLine("[WGL] ChoosePixelFormatARB not supported on this context");
}
return created_mode;
}
#endregion
#region ChoosePixelFormatPFD
static bool Compare(int got, int requested, ref int distance)
{
bool valid = true;
if (got == 0 && requested != 0)
{
// mode does not support the requested feature.
valid = false;
}
else if (got >= requested)
{
// mode supports the requested feature,
// calculate the distance from an "ideal" mode
// that matches this feature exactly.
distance += got - requested;
}
else
{
// mode supports the requested feature,
// but at a suboptimal level. For example:
// - requsted AA = 8x, got 4x
// - requested color = 32bpp, got 16bpp
// We can still use this mode but only if
// no better mode exists.
const int penalty = 8;
distance += penalty * Math.Abs(got - requested);
}
return valid;
}
static AccelerationType GetAccelerationType(ref PixelFormatDescriptor pfd)
{
AccelerationType type = AccelerationType.ICD;
if ((pfd.Flags & PixelFormatDescriptorFlags.GENERIC_FORMAT) != 0)
{
if ((pfd.Flags & PixelFormatDescriptorFlags.GENERIC_ACCELERATED) != 0)
{
type = AccelerationType.MCD;
}
else
{
type = AccelerationType.None;
}
}
return type;
}
GraphicsMode ChoosePixelFormatPFD(IntPtr device, GraphicsMode mode, AccelerationType requested_acceleration_type)
{
PixelFormatDescriptor pfd = new PixelFormatDescriptor();
PixelFormatDescriptorFlags flags = 0;
flags |= PixelFormatDescriptorFlags.DRAW_TO_WINDOW;
flags |= PixelFormatDescriptorFlags.SUPPORT_OPENGL;
if (mode.Stereo)
{
flags |= PixelFormatDescriptorFlags.STEREO;
}
if (System.Environment.OSVersion.Version.Major >= 6 &&
requested_acceleration_type != AccelerationType.None)
{
// Request a compositor-capable mode when running on
// Vista+ and using hardware acceleration. Without this,
// some modes will cause the compositor to turn off,
// which is very annoying to the user.
// Note: compositor-capable modes require hardware
// acceleration. Don't set this flag when running
// with software acceleration (e.g. over Remote Desktop
// as described in bug https://github.com/opentk/opentk/issues/35)
flags |= PixelFormatDescriptorFlags.SUPPORT_COMPOSITION;
}
int count = Functions.DescribePixelFormat(device, 1, API.PixelFormatDescriptorSize, ref pfd);
int best = 0;
int best_dist = int.MaxValue;
for (int index = 1; index <= count; index++)
{
int dist = 0;
bool valid = Functions.DescribePixelFormat(device, index, API.PixelFormatDescriptorSize, ref pfd) != 0;
valid &= GetAccelerationType(ref pfd) == requested_acceleration_type;
valid &= (pfd.Flags & flags) == flags;
valid &= pfd.PixelType == PixelType.RGBA; // indexed modes not currently supported
// heavily penalize single-buffered modes when the user requests double buffering
if ((pfd.Flags & PixelFormatDescriptorFlags.DOUBLEBUFFER) == 0 && mode.Buffers > 1)
dist += 1000;
valid &= Compare(pfd.ColorBits, mode.ColorFormat.BitsPerPixel, ref dist);
valid &= Compare(pfd.RedBits, mode.ColorFormat.Red, ref dist);
valid &= Compare(pfd.GreenBits, mode.ColorFormat.Green, ref dist);
valid &= Compare(pfd.BlueBits, mode.ColorFormat.Blue, ref dist);
valid &= Compare(pfd.AlphaBits, mode.ColorFormat.Alpha, ref dist);
valid &= Compare(pfd.AccumBits, mode.AccumulatorFormat.BitsPerPixel, ref dist);
valid &= Compare(pfd.AccumRedBits, mode.AccumulatorFormat.Red, ref dist);
valid &= Compare(pfd.AccumGreenBits, mode.AccumulatorFormat.Green, ref dist);
valid &= Compare(pfd.AccumBlueBits, mode.AccumulatorFormat.Blue, ref dist);
valid &= Compare(pfd.AccumAlphaBits, mode.AccumulatorFormat.Alpha, ref dist);
valid &= Compare(pfd.DepthBits, mode.Depth, ref dist);
valid &= Compare(pfd.StencilBits, mode.Stencil, ref dist);
if (valid && dist < best_dist)
{
best = index;
best_dist = dist;
}
}
return DescribePixelFormatPFD(device, ref pfd, best);
}
#endregion
#region DescribePixelFormatPFD
static GraphicsMode DescribePixelFormatPFD(IntPtr device, ref PixelFormatDescriptor pfd,
int pixelformat)
{
GraphicsMode created_mode = null;
if (Functions.DescribePixelFormat(device, pixelformat, pfd.Size, ref pfd) > 0)
{
created_mode = new GraphicsMode(
new IntPtr(pixelformat),
new ColorFormat(pfd.RedBits, pfd.GreenBits, pfd.BlueBits, pfd.AlphaBits),
pfd.DepthBits,
pfd.StencilBits,
0, // MSAA not supported when using PixelFormatDescriptor
new ColorFormat(pfd.AccumRedBits, pfd.AccumGreenBits, pfd.AccumBlueBits, pfd.AccumAlphaBits),
(pfd.Flags & PixelFormatDescriptorFlags.DOUBLEBUFFER) != 0 ? 2 : 1,
(pfd.Flags & PixelFormatDescriptorFlags.STEREO) != 0);
}
return created_mode;
}
#endregion
#region DescribePixelFormatARB
GraphicsMode DescribePixelFormatARB(IntPtr device, int pixelformat)
{
GraphicsMode created_mode = null;
// See http://www.opengl.org/registry/specs/ARB/wgl_pixel_format.txt for more details
if (Wgl.Delegates.wglGetPixelFormatAttribivARB != null)
{
// Define the list of attributes we are interested in. // Define the list of attributes we are interested in.
// We will use each available pixel format for these
// attributes using GetPixelFormatAttrib.
// The results will be stored in the 'values' array below. // The results will be stored in the 'values' array below.
int[] attribs = new int[] int[] attribs = new int[]
{ {
@ -205,29 +391,18 @@ namespace OpenTK.Platform.Windows
// Allocate storage for the results of GetPixelFormatAttrib queries // Allocate storage for the results of GetPixelFormatAttrib queries
int[] values = new int[attribs.Length]; int[] values = new int[attribs.Length];
// Get the number of available formats
int num_formats;
int num_formats_attrib = (int)WGL_ARB_pixel_format.NumberPixelFormatsArb;
if (Wgl.Arb.GetPixelFormatAttrib(device, 0, 0, 1, ref num_formats_attrib, out num_formats))
{
for (int p = 1; p < num_formats; p++)
{
// Get the format attributes for this pixel format // Get the format attributes for this pixel format
if (!Wgl.Arb.GetPixelFormatAttrib(device, p, 0, attribs.Length - 1, attribs, values)) if (!Wgl.Arb.GetPixelFormatAttrib(device, pixelformat, 0, attribs.Length - 1, attribs, values))
{ {
Debug.Print("[Warning] Failed to detect attributes for PixelFormat:{0}.", p); Debug.Print("[Warning] Failed to detect attributes for PixelFormat: {0}.", pixelformat);
continue;
} }
// Skip formats that don't offer full hardware acceleration // Skip formats that don't offer full hardware acceleration
WGL_ARB_pixel_format acceleration = (WGL_ARB_pixel_format)attribs[0]; WGL_ARB_pixel_format acceleration = (WGL_ARB_pixel_format)values[0];
if (acceleration != WGL_ARB_pixel_format.FullAccelerationArb) if (acceleration == WGL_ARB_pixel_format.FullAccelerationArb)
{ {
continue;
}
// Construct a new GraphicsMode to describe this format // Construct a new GraphicsMode to describe this format
GraphicsMode mode = new GraphicsMode(new IntPtr(p), created_mode = new GraphicsMode(new IntPtr(pixelformat),
new ColorFormat(values[1], values[2], values[3], values[4]), new ColorFormat(values[1], values[2], values[3], values[4]),
values[6], values[6],
values[7], values[7],
@ -235,28 +410,9 @@ namespace OpenTK.Platform.Windows
new ColorFormat(values[10], values[11], values[12], values[13]), new ColorFormat(values[10], values[11], values[12], values[13]),
values[15] == 1 ? 2 : 1, values[15] == 1 ? 2 : 1,
values[16] == 1 ? true : false); values[16] == 1 ? true : false);
yield return mode;
} }
} }
} return created_mode;
#endregion
#region ModeSelector
bool ModeSelector(GraphicsMode current, ColorFormat color, int depth, int stencil, int samples,
ColorFormat accum, int buffers, bool stereo)
{
bool result =
(color != ColorFormat.Empty ? current.ColorFormat >= color : true) &&
(depth != 0 ? current.Depth >= depth : true) &&
(stencil != 0 ? current.Stencil >= stencil : true) &&
(samples != 0 ? current.Samples >= samples : true) &&
(accum != ColorFormat.Empty ? current.AccumulatorFormat >= accum : true) &&
(buffers != 0 ? current.Buffers >= buffers : true) &&
current.Stereo == stereo;
return result;
} }
#endregion #endregion

View file

@ -164,6 +164,8 @@ namespace OpenTK.Platform.Windows
public abstract IKeyboardDriver2 KeyboardDriver { get; } public abstract IKeyboardDriver2 KeyboardDriver { get; }
public abstract IGamePadDriver GamePadDriver { get; } public abstract IGamePadDriver GamePadDriver { get; }
public abstract IJoystickDriver2 JoystickDriver { get; }
#endregion #endregion
#region IDisposable Members #region IDisposable Members

View file

@ -36,7 +36,7 @@ using System.Diagnostics;
namespace OpenTK.Platform.Windows namespace OpenTK.Platform.Windows
{ {
sealed class WinMMJoystick : IJoystickDriver, IGamePadDriver sealed class WinMMJoystick : IJoystickDriver, IJoystickDriver2
{ {
#region Fields #region Fields
@ -133,6 +133,27 @@ namespace OpenTK.Platform.Windows
return stick; return stick;
} }
void UnplugJoystick(int index)
{
// Reset the system configuration. Without this,
// joysticks that are reconnected on different
// ports are given different ids, making it
// impossible to reconnect a disconnected user.
UnsafeNativeMethods.joyConfigChanged(0);
Debug.Print("[Win] WinMM joystick {0} unplugged", index);
}
bool IsValid(int index)
{
return index >= 0 && index < UnsafeNativeMethods.joyGetNumDevs();
}
static short CalculateOffset(int pos, int min, int max)
{
int offset = (ushort.MaxValue * (pos - min)) / (max - min) - short.MaxValue;
return (short)offset;
}
#endregion #endregion
#region IJoystickDriver #region IJoystickDriver
@ -237,6 +258,95 @@ namespace OpenTK.Platform.Windows
#endregion #endregion
#region IJoystickDriver2 Members
public JoystickCapabilities GetCapabilities(int index)
{
if (IsValid(index))
{
JoyCaps mmcaps;
JoystickError result = UnsafeNativeMethods.joyGetDevCaps(index, out mmcaps, JoyCaps.SizeInBytes);
if (result == JoystickError.NoError)
{
JoystickCapabilities caps = new JoystickCapabilities(
mmcaps.NumAxes, mmcaps.NumButtons, true);
//if ((caps.Capabilities & JoystCapsFlags.HasPov) != 0)
// gpcaps.DPadCount++;
return caps;
}
else if (result == JoystickError.Unplugged)
{
UnplugJoystick(index);
}
}
else
{
Debug.Print("[Win] Invalid WinMM joystick device {0}", index);
}
return new JoystickCapabilities();
}
public JoystickState GetState(int index)
{
JoystickState state = new JoystickState();
if (IsValid(index))
{
JoyInfoEx info = new JoyInfoEx();
info.Size = JoyInfoEx.SizeInBytes;
info.Flags = JoystickFlags.All;
JoystickError result = UnsafeNativeMethods.joyGetPosEx(index, ref info);
if (result == JoystickError.NoError)
{
JoyCaps caps;
result = UnsafeNativeMethods.joyGetDevCaps(index, out caps, JoyCaps.SizeInBytes);
if (result == JoystickError.NoError)
{
state.SetAxis(JoystickAxis.Axis0, CalculateOffset(info.XPos, caps.XMin, caps.XMax));
state.SetAxis(JoystickAxis.Axis1, CalculateOffset(info.YPos, caps.YMin, caps.YMax));
state.SetAxis(JoystickAxis.Axis2, CalculateOffset(info.ZPos, caps.ZMin, caps.ZMax));
state.SetAxis(JoystickAxis.Axis3, CalculateOffset(info.RPos, caps.RMin, caps.RMax));
state.SetAxis(JoystickAxis.Axis4, CalculateOffset(info.UPos, caps.UMin, caps.UMax));
state.SetAxis(JoystickAxis.Axis5, CalculateOffset(info.VPos, caps.VMin, caps.VMax));
for (int i = 0; i < 16; i++)
{
state.SetButton(JoystickButton.Button0 + i, (info.Buttons & 1 << i) != 0);
}
state.SetIsConnected(true);
}
}
if (result == JoystickError.Unplugged)
{
UnplugJoystick(index);
}
}
else
{
Debug.Print("[Win] Invalid WinMM joystick device {0}", index);
}
return state;
}
public Guid GetGuid(int index)
{
Guid guid = new Guid();
if (IsValid(index))
{
// Todo: implement WinMM Guid retrieval
}
return guid;
}
#endregion
#region IDisposable #region IDisposable
public void Dispose() public void Dispose()
@ -378,9 +488,11 @@ namespace OpenTK.Platform.Windows
[DllImport("Winmm.dll"), SuppressUnmanagedCodeSecurity] [DllImport("Winmm.dll"), SuppressUnmanagedCodeSecurity]
public static extern JoystickError joyGetDevCaps(int uJoyID, out JoyCaps pjc, int cbjc); public static extern JoystickError joyGetDevCaps(int uJoyID, out JoyCaps pjc, int cbjc);
[DllImport("Winmm.dll"), SuppressUnmanagedCodeSecurity] [DllImport("Winmm.dll"), SuppressUnmanagedCodeSecurity]
public static extern uint joyGetPosEx(int uJoyID, ref JoyInfoEx pji); public static extern JoystickError joyGetPosEx(int uJoyID, ref JoyInfoEx pji);
[DllImport("Winmm.dll"), SuppressUnmanagedCodeSecurity] [DllImport("Winmm.dll"), SuppressUnmanagedCodeSecurity]
public static extern int joyGetNumDevs(); public static extern int joyGetNumDevs();
[DllImport("Winmm.dll"), SuppressUnmanagedCodeSecurity]
public static extern JoystickError joyConfigChanged(int flags);
} }
#endregion #endregion
@ -427,21 +539,5 @@ namespace OpenTK.Platform.Windows
} }
#endregion #endregion
//HACK implement
public GamePadState GetState()
{
throw new NotImplementedException();
}
public GamePadState GetState(int index)
{
throw new NotImplementedException();
}
public string GetDeviceName(int index)
{
throw new NotImplementedException();
}
} }
} }

View file

@ -43,7 +43,8 @@ namespace OpenTK.Platform.Windows
WinRawKeyboard keyboard_driver; WinRawKeyboard keyboard_driver;
WinRawMouse mouse_driver; WinRawMouse mouse_driver;
WinMMJoystick joystick_driver; IGamePadDriver gamepad_driver;
IJoystickDriver2 joystick_driver;
IntPtr DevNotifyHandle; IntPtr DevNotifyHandle;
static readonly Guid DeviceInterfaceHid = new Guid("4D1E55B2-F16F-11CF-88CB-001111000030"); static readonly Guid DeviceInterfaceHid = new Guid("4D1E55B2-F16F-11CF-88CB-001111000030");
@ -138,6 +139,15 @@ namespace OpenTK.Platform.Windows
keyboard_driver = new WinRawKeyboard(Parent.Handle); keyboard_driver = new WinRawKeyboard(Parent.Handle);
mouse_driver = new WinRawMouse(Parent.Handle); mouse_driver = new WinRawMouse(Parent.Handle);
joystick_driver = new WinMMJoystick(); joystick_driver = new WinMMJoystick();
try
{
gamepad_driver = new XInputJoystick();
}
catch (Exception e)
{
Debug.Print("[Win] XInput driver not supported, falling back to WinMM");
gamepad_driver = new MappedGamePadDriver();
}
DevNotifyHandle = RegisterForDeviceNotifications(Parent); DevNotifyHandle = RegisterForDeviceNotifications(Parent);
} }
@ -186,6 +196,11 @@ namespace OpenTK.Platform.Windows
} }
public override IGamePadDriver GamePadDriver public override IGamePadDriver GamePadDriver
{
get { return gamepad_driver; }
}
public override IJoystickDriver2 JoystickDriver
{ {
get { return joystick_driver; } get { return joystick_driver; }
} }

View file

@ -103,10 +103,11 @@ namespace OpenTK.Platform.Windows
// This is a keyboard or USB keyboard device. In the latter case, discover if it really is a // This is a keyboard or USB keyboard device. In the latter case, discover if it really is a
// keyboard device by qeurying the registry. // keyboard device by qeurying the registry.
RegistryKey regkey = GetRegistryKey(name); RegistryKey regkey = GetRegistryKey(name);
if (regkey == null)
continue;
string deviceDesc = (string)regkey.GetValue("DeviceDesc"); string deviceDesc = (string)regkey.GetValue("DeviceDesc");
string deviceClass = (string)regkey.GetValue("Class"); string deviceClass = (string)regkey.GetValue("Class");
string deviceClassGUID = (string)regkey.GetValue("ClassGUID"); // for windows 8 support via OpenTK issue 3198 string deviceClassGUID = (string)regkey.GetValue("ClassGUID"); // for windows 8 support via OpenTK issue 3198
// making a guess at backwards compatability. Not sure what older windows returns in these cases... // making a guess at backwards compatability. Not sure what older windows returns in these cases...
@ -205,10 +206,15 @@ namespace OpenTK.Platform.Windows
static RegistryKey GetRegistryKey(string name) static RegistryKey GetRegistryKey(string name)
{ {
if (name.Length < 4)
return null;
// remove the \??\ // remove the \??\
name = name.Substring(4); name = name.Substring(4);
string[] split = name.Split('#'); string[] split = name.Split('#');
if (split.Length < 3)
return null;
string id_01 = split[0]; // ACPI (Class code) string id_01 = split[0]; // ACPI (Class code)
string id_02 = split[1]; // PNP0303 (SubClass code) string id_02 = split[1]; // PNP0303 (SubClass code)

View file

@ -110,11 +110,13 @@ namespace OpenTK.Platform.Windows
// This is a mouse or a USB mouse device. In the latter case, discover if it really is a // This is a mouse or a USB mouse device. In the latter case, discover if it really is a
// mouse device by qeurying the registry. // mouse device by qeurying the registry.
RegistryKey regkey = FindRegistryKey(name); RegistryKey regkey = FindRegistryKey(name);
if (regkey == null)
continue;
string deviceDesc = (string)regkey.GetValue("DeviceDesc"); string deviceDesc = (string)regkey.GetValue("DeviceDesc");
string deviceClass = (string)regkey.GetValue("Class") as string; string deviceClass = (string)regkey.GetValue("Class") as string;
if(deviceClass == null){ if(deviceClass == null)
{
// Added to address OpenTK issue 3198 with mouse on Windows 8 // Added to address OpenTK issue 3198 with mouse on Windows 8
string deviceClassGUID = (string)regkey.GetValue("ClassGUID"); string deviceClassGUID = (string)regkey.GetValue("ClassGUID");
RegistryKey classGUIDKey = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Class\" + deviceClassGUID); RegistryKey classGUIDKey = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Class\" + deviceClassGUID);
@ -266,10 +268,15 @@ namespace OpenTK.Platform.Windows
static RegistryKey FindRegistryKey(string name) static RegistryKey FindRegistryKey(string name)
{ {
if (name.Length < 4)
return null;
// remove the \??\ // remove the \??\
name = name.Substring(4); name = name.Substring(4);
string[] split = name.Split('#'); string[] split = name.Split('#');
if (split.Length < 3)
return null;
string id_01 = split[0]; // ACPI (Class code) string id_01 = split[0]; // ACPI (Class code)
string id_02 = split[1]; // PNP0303 (SubClass code) string id_02 = split[1]; // PNP0303 (SubClass code)

View file

@ -0,0 +1,405 @@
#region License
//
// XInputJoystick.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-2013 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;
using System.Collections.Generic;
using System.Text;
using OpenTK.Input;
using System.Runtime.InteropServices;
using System.Security;
using System.Diagnostics;
namespace OpenTK.Platform.Windows
{
class XInputJoystick : IGamePadDriver, IDisposable
{
XInput xinput = new XInput();
#region IGamePadDriver Members
public GamePadState GetState(int index)
{
XInputState xstate;
XInputErrorCode error = xinput.GetState((XInputUserIndex)index, out xstate);
GamePadState state = new GamePadState();
if (error == XInputErrorCode.Success)
{
state.SetConnected(true);
state.SetAxis(GamePadAxes.LeftX, xstate.GamePad.ThumbLX);
state.SetAxis(GamePadAxes.LeftY, xstate.GamePad.ThumbLY);
state.SetAxis(GamePadAxes.RightX, xstate.GamePad.ThumbRX);
state.SetAxis(GamePadAxes.RightY, xstate.GamePad.ThumbRY);
state.SetTriggers(xstate.GamePad.LeftTrigger, xstate.GamePad.RightTrigger);
state.SetButton(TranslateButtons(xstate.GamePad.Buttons), true);
}
return state;
}
public GamePadCapabilities GetCapabilities(int index)
{
XInputDeviceCapabilities xcaps;
XInputErrorCode error = xinput.GetCapabilities(
(XInputUserIndex)index,
XInputCapabilitiesFlags.Default,
out xcaps);
if (error == XInputErrorCode.Success)
{
GamePadType type = TranslateSubType(xcaps.SubType);
Buttons buttons = TranslateButtons(xcaps.GamePad.Buttons);
GamePadAxes axes = TranslateAxes(ref xcaps.GamePad);
return new GamePadCapabilities(type, axes, buttons, true);
}
return new GamePadCapabilities();
}
public string GetName(int index)
{
return String.Empty;
}
public bool SetVibration(int index, float left, float right)
{
return false;
}
#endregion
#region Private Members
GamePadAxes TranslateAxes(ref XInputGamePad pad)
{
GamePadAxes axes = 0;
axes |= pad.ThumbLX != 0 ? GamePadAxes.LeftX : 0;
axes |= pad.ThumbLY != 0 ? GamePadAxes.LeftY : 0;
axes |= pad.LeftTrigger != 0 ? GamePadAxes.LeftTrigger : 0;
axes |= pad.ThumbRX != 0 ? GamePadAxes.RightX : 0;
axes |= pad.ThumbRY != 0 ? GamePadAxes.RightY : 0;
axes |= pad.RightTrigger != 0 ? GamePadAxes.RightTrigger : 0;
return axes;
}
Buttons TranslateButtons(XInputButtons xbuttons)
{
Buttons buttons = 0;
buttons |= (xbuttons & XInputButtons.A) != 0 ? Buttons.A : 0;
buttons |= (xbuttons & XInputButtons.B) != 0 ? Buttons.B : 0;
buttons |= (xbuttons & XInputButtons.X) != 0 ? Buttons.X : 0;
buttons |= (xbuttons & XInputButtons.Y) != 0 ? Buttons.Y : 0;
buttons |= (xbuttons & XInputButtons.Start) != 0 ? Buttons.Start : 0;
buttons |= (xbuttons & XInputButtons.Back) != 0 ? Buttons.Back : 0;
//buttons |= (xbuttons & XInputButtons.BigButton) != 0 ? Buttons.BigButton : 0;
buttons |= (xbuttons & XInputButtons.LeftShoulder) != 0 ? Buttons.LeftShoulder : 0;
buttons |= (xbuttons & XInputButtons.RightShoulder) != 0 ? Buttons.RightShoulder : 0;
buttons |= (xbuttons & XInputButtons.LeftThumb) != 0 ? Buttons.LeftStick : 0;
buttons |= (xbuttons & XInputButtons.RightThumb) != 0 ? Buttons.RightStick : 0;
buttons |= (xbuttons & XInputButtons.DPadDown) != 0 ? Buttons.DPadDown : 0;
buttons |= (xbuttons & XInputButtons.DPadUp) != 0 ? Buttons.DPadUp : 0;
buttons |= (xbuttons & XInputButtons.DPadLeft) != 0 ? Buttons.DPadLeft : 0;
buttons |= (xbuttons & XInputButtons.DPadRight) != 0 ? Buttons.DPadRight : 0;
return buttons;
}
GamePadType TranslateSubType(XInputDeviceSubType xtype)
{
switch (xtype)
{
case XInputDeviceSubType.ArcadePad: return GamePadType.ArcadePad;
case XInputDeviceSubType.ArcadeStick: return GamePadType.ArcadeStick;
case XInputDeviceSubType.DancePad: return GamePadType.DancePad;
case XInputDeviceSubType.DrumKit: return GamePadType.DrumKit;
case XInputDeviceSubType.FlightStick: return GamePadType.FlightStick;
case XInputDeviceSubType.GamePad: return GamePadType.GamePad;
case XInputDeviceSubType.Guitar: return GamePadType.Guitar;
case XInputDeviceSubType.GuitarAlternate: return GamePadType.AlternateGuitar;
case XInputDeviceSubType.GuitarBass: return GamePadType.BassGuitar;
case XInputDeviceSubType.Wheel: return GamePadType.Wheel;
case XInputDeviceSubType.Unknown:
default:
return GamePadType.Unknown;
}
}
enum XInputErrorCode
{
Success = 0,
DeviceNotConnected
}
enum XInputDeviceType : byte
{
GamePad
}
enum XInputDeviceSubType : byte
{
Unknown = 0,
GamePad = 1,
Wheel = 2,
ArcadeStick = 3,
FlightStick = 4,
DancePad = 5,
Guitar = 6,
GuitarAlternate = 7,
DrumKit = 8,
GuitarBass = 0xb,
ArcadePad = 0x13
}
enum XInputCapabilities
{
ForceFeedback = 0x0001,
Wireless = 0x0002,
Voice = 0x0004,
PluginModules = 0x0008,
NoNavigation = 0x0010,
}
enum XInputButtons : ushort
{
DPadUp = 0x0001,
DPadDown = 0x0002,
DPadLeft = 0x0004,
DPadRight = 0x0008,
Start = 0x0010,
Back = 0x0020,
LeftThumb = 0x0040,
RightThumb = 0x0080,
LeftShoulder = 0x0100,
RightShoulder = 0x0200,
A = 0x1000,
B = 0x2000,
X = 0x4000,
Y = 0x8000
}
[Flags]
enum XInputCapabilitiesFlags
{
Default = 0,
GamePadOnly = 1
}
enum XInputBatteryType : byte
{
Disconnected = 0x00,
Wired = 0x01,
Alkaline = 0x02,
NiMH = 0x03,
Unknown = 0xff
}
enum XInputBatteryLevel : byte
{
Empty = 0x00,
Low = 0x01,
Medium = 0x02,
Full = 0x03
}
enum XInputUserIndex
{
First = 0,
Second,
Third,
Fourth,
Any = 0xff
}
struct XInputThresholds
{
public const int LeftThumbDeadzone = 7849;
public const int RightThumbDeadzone = 8689;
public const int TriggerThreshold = 30;
}
struct XInputGamePad
{
public XInputButtons Buttons;
public byte LeftTrigger;
public byte RightTrigger;
public short ThumbLX;
public short ThumbLY;
public short ThumbRX;
public short ThumbRY;
}
struct XInputState
{
public int PacketNumber;
public XInputGamePad GamePad;
}
struct XInputVibration
{
public short LeftMotorSpeed;
public short RightMotorSpeed;
}
struct XInputDeviceCapabilities
{
public XInputDeviceType Type;
public XInputDeviceSubType SubType;
public short Flags;
public XInputGamePad GamePad;
public XInputVibration Vibration;
}
struct XInputBatteryInformation
{
public XInputBatteryType Type;
public XInputBatteryLevel Level;
}
class XInput : IDisposable
{
IntPtr dll;
internal XInput()
{
// Try to load the newest XInput***.dll installed on the system
// The delegates below will be loaded dynamically from that dll
dll = Functions.LoadLibrary("XINPUT1_4");
if (dll == IntPtr.Zero)
dll = Functions.LoadLibrary("XINPUT1_3");
if (dll == IntPtr.Zero)
dll = Functions.LoadLibrary("XINPUT1_2");
if (dll == IntPtr.Zero)
dll = Functions.LoadLibrary("XINPUT1_1");
if (dll == IntPtr.Zero)
dll = Functions.LoadLibrary("XINPUT9_1_0");
if (dll == IntPtr.Zero)
throw new NotSupportedException("XInput was not found on this platform");
// Load the entry points we are interested in from that dll
GetCapabilities = (XInputGetCapabilities)Load("XInputGetCapabilities", typeof(XInputGetCapabilities));
GetState = (XInputGetState)Load("XInputGetState", typeof(XInputGetState));
SetState = (XInputSetState)Load("XInputSetState", typeof(XInputSetState));
}
#region Private Members
Delegate Load(string name, Type type)
{
IntPtr pfunc = Functions.GetProcAddress(dll, name);
if (pfunc != IntPtr.Zero)
return Marshal.GetDelegateForFunctionPointer(pfunc, type);
return null;
}
#endregion
#region Internal Members
internal XInputGetCapabilities GetCapabilities;
internal XInputGetState GetState;
internal XInputSetState SetState;
[SuppressUnmanagedCodeSecurity]
internal delegate XInputErrorCode XInputGetCapabilities(
XInputUserIndex dwUserIndex,
XInputCapabilitiesFlags dwFlags,
out XInputDeviceCapabilities pCapabilities);
[SuppressUnmanagedCodeSecurity]
internal delegate XInputErrorCode XInputGetState
(
XInputUserIndex dwUserIndex,
out XInputState pState
);
[SuppressUnmanagedCodeSecurity]
internal delegate XInputErrorCode XInputSetState
(
XInputUserIndex dwUserIndex,
ref XInputVibration pVibration
);
#endregion
#region IDisposable Members
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
void Dispose(bool manual)
{
if (manual)
{
if (dll != IntPtr.Zero)
{
Functions.FreeLibrary(dll);
dll = IntPtr.Zero;
}
}
}
#endregion
}
#endregion
#region IDisposable Members
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
void Dispose(bool manual)
{
if (manual)
{
xinput.Dispose();
}
else
{
Debug.Print("{0} leaked, did you forget to call Dispose()?", typeof(XInputJoystick).Name);
}
}
#if DEBUG
~XInputJoystick()
{
Dispose(false);
}
#endif
#endregion
}
}

View file

@ -221,7 +221,20 @@ namespace OpenTK.Platform.X11
int current_resolution_index = Functions.XRRConfigCurrentConfiguration(screen_config, out current_rotation); int current_resolution_index = Functions.XRRConfigCurrentConfiguration(screen_config, out current_rotation);
if (dev.Bounds == Rectangle.Empty) if (dev.Bounds == Rectangle.Empty)
dev.Bounds = new Rectangle(0, 0, available_res[current_resolution_index].Width, available_res[current_resolution_index].Height); {
// We have added depths.Length copies of each resolution
// Adjust the return value of XRRGetScreenInfo to retrieve the correct resolution
int index = current_resolution_index * depths.Length;
// Make sure we are within the bounds of the available_res array
if (index >= available_res.Count)
{
// If not, use the return value of XRRGetScreenInfo directly
index = current_resolution_index;
}
DisplayResolution current_resolution = available_res[index];
dev.Bounds = new Rectangle(0, 0, current_resolution.Width, current_resolution.Height);
}
dev.BitsPerPixel = current_depth; dev.BitsPerPixel = current_depth;
dev.RefreshRate = current_refresh_rate; dev.RefreshRate = current_refresh_rate;
dev.AvailableResolutions = available_res; dev.AvailableResolutions = available_res;

View file

@ -98,6 +98,12 @@ namespace OpenTK.Platform.X11
return new X11Joystick(); return new X11Joystick();
} }
public virtual OpenTK.Input.IJoystickDriver2 CreateJoystickDriver()
{
return new X11Joystick();
}
#endregion #endregion
#region IDisposable Members #region IDisposable Members

View file

@ -59,6 +59,7 @@ namespace OpenTK.Platform.X11
// Legacy input support // Legacy input support
X11Input driver; X11Input driver;
KeyboardDevice keyboard;
MouseDevice mouse; MouseDevice mouse;
// Window manager hints for fullscreen windows. // Window manager hints for fullscreen windows.
@ -119,6 +120,8 @@ namespace OpenTK.Platform.X11
readonly byte[] ascii = new byte[16]; readonly byte[] ascii = new byte[16];
readonly char[] chars = new char[16]; readonly char[] chars = new char[16];
readonly KeyPressEventArgs KPEventArgs = new KeyPressEventArgs('\0'); readonly KeyPressEventArgs KPEventArgs = new KeyPressEventArgs('\0');
readonly KeyboardKeyEventArgs KeyDownEventArgs = new KeyboardKeyEventArgs();
readonly KeyboardKeyEventArgs KeyUpEventArgs = new KeyboardKeyEventArgs();
readonly IntPtr EmptyCursor; readonly IntPtr EmptyCursor;
@ -209,6 +212,7 @@ namespace OpenTK.Platform.X11
RefreshWindowBounds(ref e); RefreshWindowBounds(ref e);
driver = new X11Input(window); driver = new X11Input(window);
keyboard = driver.Keyboard[0];
mouse = driver.Mouse[0]; mouse = driver.Mouse[0];
EmptyCursor = CreateEmptyCursor(window); EmptyCursor = CreateEmptyCursor(window);
@ -311,6 +315,11 @@ namespace OpenTK.Platform.X11
#region SetWindowMinMax #region SetWindowMinMax
void SetWindowMinMax(int min_width, int min_height, int max_width, int max_height)
{
SetWindowMinMax((short)min_width, (short)min_height, (short)max_width, (short)max_height);
}
void SetWindowMinMax(short min_width, short min_height, short max_width, short max_height) void SetWindowMinMax(short min_width, short min_height, short max_width, short max_height)
{ {
IntPtr dummy; IntPtr dummy;
@ -616,10 +625,26 @@ namespace OpenTK.Platform.X11
#endregion #endregion
bool RefreshWindowBorders() bool RefreshWindowBorders()
{
bool borders_changed = false;
if (IsWindowBorderHidden)
{
borders_changed =
border_left != 0 ||
border_right != 0 ||
border_top != 0 ||
border_bottom != 0;
border_left = 0;
border_right = 0;
border_top = 0;
border_bottom = 0;
}
else
{ {
IntPtr atom, nitems, bytes_after, prop = IntPtr.Zero; IntPtr atom, nitems, bytes_after, prop = IntPtr.Zero;
int format; int format;
bool borders_changed = false;
using (new XLock(window.Display)) using (new XLock(window.Display))
{ {
@ -659,6 +684,7 @@ namespace OpenTK.Platform.X11
Functions.XFree(prop); Functions.XFree(prop);
} }
} }
}
return borders_changed; return borders_changed;
} }
@ -667,9 +693,30 @@ namespace OpenTK.Platform.X11
{ {
RefreshWindowBorders(); RefreshWindowBorders();
// For whatever reason, the x/y coordinates
// of a configure event are global to the
// root window when it is a send_event but
// local when it is a regular event.
// I don't know who designed this, but this is
// utter nonsense.
int x, y;
IntPtr unused;
if (!e.ConfigureEvent.send_event)
{
Functions.XTranslateCoordinates(window.Display,
window.Handle, window.RootWindow,
0, 0, out x, out y, out unused);
}
else
{
x = e.ConfigureEvent.x;
y = e.ConfigureEvent.y;
}
Point new_location = new Point( Point new_location = new Point(
e.ConfigureEvent.x - border_left, x - border_left,
e.ConfigureEvent.y - border_top); y - border_top);
if (Location != new_location) if (Location != new_location)
{ {
bounds.Location = new_location; bounds.Location = new_location;
@ -688,6 +735,8 @@ namespace OpenTK.Platform.X11
Resize(this, EventArgs.Empty); Resize(this, EventArgs.Empty);
} }
//Debug.Print("[X11] Window bounds changed: {0}", bounds);
} }
static IntPtr CreateEmptyCursor(X11WindowInfo window) static IntPtr CreateEmptyCursor(X11WindowInfo window)
@ -795,27 +844,48 @@ namespace OpenTK.Platform.X11
break; break;
case XEventName.KeyPress: case XEventName.KeyPress:
driver.ProcessEvent(ref e); case XEventName.KeyRelease:
bool pressed = e.type == XEventName.KeyPress;
Key key;
if (driver.TranslateKey(ref e.KeyEvent, out key))
{
if (pressed)
{
// Raise KeyDown event
KeyDownEventArgs.Key = key;
KeyDownEventArgs.ScanCode = (uint)e.KeyEvent.keycode;
KeyDown(this, KeyDownEventArgs);
}
else
{
// Raise KeyUp event
KeyUpEventArgs.Key = key;
KeyUpEventArgs.ScanCode = (uint)e.KeyEvent.keycode;
KeyUp(this, KeyDownEventArgs);
}
// Update legacy GameWindow.Keyboard API:
keyboard.SetKey(key, (uint)e.KeyEvent.keycode, pressed);
if (pressed)
{
// Translate XKeyPress to characters and
// raise KeyPress events
int status = 0; int status = 0;
status = Functions.XLookupString(ref e.KeyEvent, ascii, ascii.Length, null, IntPtr.Zero); status = Functions.XLookupString(
ref e.KeyEvent, ascii, ascii.Length, null, IntPtr.Zero);
Encoding.Default.GetChars(ascii, 0, status, chars, 0); Encoding.Default.GetChars(ascii, 0, status, chars, 0);
EventHandler<KeyPressEventArgs> key_press = KeyPress;
if (key_press != null)
{
for (int i = 0; i < status; i++) for (int i = 0; i < status; i++)
{
if (!Char.IsControl(chars[i]))
{ {
KPEventArgs.KeyChar = chars[i]; KPEventArgs.KeyChar = chars[i];
key_press(this, KPEventArgs); KeyPress(this, KPEventArgs);
}
}
} }
} }
break;
case XEventName.KeyRelease:
// Todo: raise KeyPress event. Use code from
// http://anonsvn.mono-project.com/viewvc/trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/X11Keyboard.cs?view=markup
driver.ProcessEvent(ref e);
break; break;
case XEventName.MotionNotify: case XEventName.MotionNotify:
@ -926,17 +996,44 @@ namespace OpenTK.Platform.X11
public Rectangle Bounds public Rectangle Bounds
{ {
get { return bounds; } get
{
return bounds;
}
set set
{ {
bool is_location_changed = bounds.Location != value.Location;
bool is_size_changed = bounds.Size != value.Size;
int x = value.X;
int y = value.Y;
int width = value.Width - border_left - border_right;
int height = value.Height - border_top - border_bottom;
if (WindowBorder != WindowBorder.Resizable)
{
SetWindowMinMax(width, height, width, height);
}
using (new XLock(window.Display)) using (new XLock(window.Display))
{
if (is_location_changed && is_size_changed)
{ {
Functions.XMoveResizeWindow(window.Display, window.Handle, Functions.XMoveResizeWindow(window.Display, window.Handle,
value.X, x, y, width, height);
value.Y,
value.Width - border_left - border_right,
value.Height - border_top - border_bottom);
} }
else if (is_location_changed)
{
Functions.XMoveWindow(window.Display, window.Handle,
x, y);
}
else if (is_size_changed)
{
Functions.XResizeWindow(window.Display, window.Handle,
width, height);
}
}
ProcessEvents(); ProcessEvents();
} }
} }
@ -950,11 +1047,7 @@ namespace OpenTK.Platform.X11
get { return Bounds.Location; } get { return Bounds.Location; }
set set
{ {
using (new XLock(window.Display)) Bounds = new Rectangle(value, Bounds.Size);
{
Functions.XMoveWindow(window.Display, window.Handle, value.X, value.Y);
}
ProcessEvents();
} }
} }
@ -967,16 +1060,7 @@ namespace OpenTK.Platform.X11
get { return Bounds.Size; } get { return Bounds.Size; }
set set
{ {
int width = value.Width - border_left - border_right; Bounds = new Rectangle(Bounds.Location, value);
int height = value.Height - border_top - border_bottom;
width = width <= 0 ? 1 : width;
height = height <= 0 ? 1 : height;
using (new XLock(window.Display))
{
Functions.XResizeWindow(window.Display, window.Handle, width, height);
}
ProcessEvents();
} }
} }

View file

@ -40,6 +40,8 @@ namespace OpenTK.Platform.X11
IntPtr visual = IntPtr.Zero; IntPtr visual = IntPtr.Zero;
IntPtr display = API.DefaultDisplay; IntPtr display = API.DefaultDisplay;
do
{
// Try to select a visual using Glx.ChooseFBConfig and Glx.GetVisualFromFBConfig. // Try to select a visual using Glx.ChooseFBConfig and Glx.GetVisualFromFBConfig.
// This is only supported on GLX 1.3 - if it fails, fall back to Glx.ChooseVisual. // This is only supported on GLX 1.3 - if it fails, fall back to Glx.ChooseVisual.
visual = SelectVisualUsingFBConfig(color, depth, stencil, samples, accum, buffers, stereo); visual = SelectVisualUsingFBConfig(color, depth, stencil, samples, accum, buffers, stereo);
@ -48,7 +50,54 @@ namespace OpenTK.Platform.X11
visual = SelectVisualUsingChooseVisual(color, depth, stencil, samples, accum, buffers, stereo); visual = SelectVisualUsingChooseVisual(color, depth, stencil, samples, accum, buffers, stereo);
if (visual == IntPtr.Zero) if (visual == IntPtr.Zero)
{
// Relax parameters and retry
if (stereo)
{
stereo = false;
continue;
}
if (accum != 0)
{
accum = 0;
continue;
}
if (samples > 0)
{
samples = Math.Max(samples - 2, 0);
continue;
}
if (stencil != 0)
{
stencil = 0;
continue;
}
if (depth != 0)
{
depth = 0;
continue;
}
if (color != 24)
{
color = 24;
continue;
}
if (buffers != 0)
{
buffers = 0;
continue;
}
throw new GraphicsModeException("Requested GraphicsMode not available."); throw new GraphicsModeException("Requested GraphicsMode not available.");
}
}
while (visual == IntPtr.Zero);
XVisualInfo info = (XVisualInfo)Marshal.PtrToStructure(visual, typeof(XVisualInfo)); XVisualInfo info = (XVisualInfo)Marshal.PtrToStructure(visual, typeof(XVisualInfo));

View file

@ -78,7 +78,6 @@ namespace OpenTK.Platform.X11
Marshal.PtrToStructure(keysym_ptr, keysyms); Marshal.PtrToStructure(keysym_ptr, keysyms);
API.Free(keysym_ptr); API.Free(keysym_ptr);
KeyboardDevice kb = new KeyboardDevice();
keyboard.Description = "Default X11 keyboard"; keyboard.Description = "Default X11 keyboard";
keyboard.NumberOfKeys = lastKeyCode - firstKeyCode + 1; keyboard.NumberOfKeys = lastKeyCode - firstKeyCode + 1;
keyboard.DeviceID = IntPtr.Zero; keyboard.DeviceID = IntPtr.Zero;
@ -148,29 +147,38 @@ namespace OpenTK.Platform.X11
#endif #endif
#endregion #endregion
#region TranslateKey
internal bool TranslateKey(ref XKeyEvent e, out Key key)
{
XKey keysym = (XKey)API.LookupKeysym(ref e, 0);
XKey keysym2 = (XKey)API.LookupKeysym(ref e, 1);
key = Key.Unknown;
if (keymap.ContainsKey(keysym))
{
key = keymap[keysym];
}
else if (keymap.ContainsKey(keysym2))
{
key = keymap[keysym2];
}
else
{
Debug.Print("KeyCode {0} (Keysym: {1}, {2}) not mapped.", e.keycode, (XKey)keysym, (XKey)keysym2);
}
return key != Key.Unknown;
}
#endregion
#region internal void ProcessEvent(ref XEvent e) #region internal void ProcessEvent(ref XEvent e)
internal void ProcessEvent(ref XEvent e) internal void ProcessEvent(ref XEvent e)
{ {
switch (e.type) switch (e.type)
{ {
case XEventName.KeyPress:
case XEventName.KeyRelease:
bool pressed = e.type == XEventName.KeyPress;
XKey keysym = (XKey)API.LookupKeysym(ref e.KeyEvent, 0);
XKey keysym2 = (XKey)API.LookupKeysym(ref e.KeyEvent, 1);
Key key = Key.Unknown;
if (keymap.ContainsKey(keysym))
key = keymap[keysym];
else if (keymap.ContainsKey(keysym2))
key = keymap[keysym2];
else
Debug.Print("KeyCode {0} (Keysym: {1}, {2}) not mapped.", e.KeyEvent.keycode, (XKey)keysym, (XKey)keysym2);
keyboard.SetKey(key, (uint)e.KeyEvent.keycode, pressed);
break;
case XEventName.ButtonPress: case XEventName.ButtonPress:
if (e.ButtonEvent.button == 1) mouse[OpenTK.Input.MouseButton.Left] = true; if (e.ButtonEvent.button == 1) mouse[OpenTK.Input.MouseButton.Left] = true;
else if (e.ButtonEvent.button == 2) mouse[OpenTK.Input.MouseButton.Middle] = true; else if (e.ButtonEvent.button == 2) mouse[OpenTK.Input.MouseButton.Middle] = true;

View file

@ -36,7 +36,7 @@ namespace OpenTK.Platform.X11
{ {
struct X11JoyDetails { } struct X11JoyDetails { }
sealed class X11Joystick : IJoystickDriver, IGamePadDriver sealed class X11Joystick : IJoystickDriver, IJoystickDriver2, IGamePadDriver
{ {
#region Fields #region Fields
@ -259,21 +259,47 @@ namespace OpenTK.Platform.X11
#endregion #endregion
//HACK implement #region IGamePadDriver Members
public GamePadState GetState()
public GamePadCapabilities GetCapabilities(int index)
{ {
throw new NotImplementedException(); return new GamePadCapabilities();
} }
public GamePadState GetState(int index) public GamePadState GetState(int index)
{ {
Poll(); return new GamePadState();
throw new NotImplementedException();
} }
public string GetDeviceName(int index) public string GetName(int index)
{ {
throw new NotImplementedException(); return String.Empty;
} }
public bool SetVibration(int index, float left, float right)
{
return false;
}
#endregion
#region IJoystickDriver2 Members
JoystickState IJoystickDriver2.GetState(int index)
{
return new JoystickState();
}
JoystickCapabilities IJoystickDriver2.GetCapabilities(int index)
{
return new JoystickCapabilities();
}
Guid IJoystickDriver2.GetGuid(int index)
{
return new Guid();
}
#endregion
} }
} }

View file

@ -84,7 +84,7 @@ namespace OpenTK
/// Set to false for applications that are not /// Set to false for applications that are not
/// DPI-aware (e.g. WinForms.) /// DPI-aware (e.g. WinForms.)
/// </summary> /// </summary>
/// <seealso cref="http://msdn.microsoft.com/en-us/library/windows/desktop/ee308410(v=vs.85).aspx"/> /// See: http://msdn.microsoft.com/en-us/library/windows/desktop/ee308410(v=vs.85).aspx
public bool EnableHighResolution { get; set; } public bool EnableHighResolution { get; set; }
/// <summary> /// <summary>