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
*.vssscc
.builds
*.pidb
# Visual C++ cache files
ipch/

View file

@ -565,6 +565,7 @@
<None Include="..\..\Dependencies\x64\libSDL2.dylib">
<Link>Dependencies\x64\libSDL2.dylib</Link>
</None>
<Compile Include="OpenTK\Test\ExternalContext.cs" />
</ItemGroup>
<ItemGroup>
<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;
bool mouse_in_window = false;
bool viewport_changed = true;
bool refresh_text = true;
MouseState mouse, mouse_old;
KeyboardState keyboard, keyboard_old;
// time drift
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()
: base(800, 600, GraphicsMode.Default)
@ -36,24 +49,13 @@ namespace Examples.Tests
Keyboard.KeyRepeat = true;
KeyDown += KeyDownHandler;
KeyPress += KeyPressHandler;
MouseEnter += delegate { mouse_in_window = true; };
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.ButtonDown += 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)
@ -94,18 +96,24 @@ namespace Examples.Tests
case Key.KeypadMinus:
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)
{
refresh_text = true;
}
void MouseButtonHandler(object sender, MouseButtonEventArgs e)
{
refresh_text = true;
if (e.Button == MouseButton.Left && e.IsPressed)
{
CursorVisible = false;
@ -116,142 +124,223 @@ namespace Examples.Tests
{
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));
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:";
float space = gfx.MeasureString(" ", TextFont).Width;
float offset = gfx.MeasureString(str, TextFont).Width + space;
DrawString(gfx, str, line);
for (int i = 0; i < (int)Key.LastKey; i++)
line++;
DrawString(gfx, "Keyboard:", line++);
for (int i = 0; i < 4; i++)
{
Key k = (Key)i;
if (keyboard[k])
var state = OpenTK.Input.Keyboard.GetState(i);
if (state.IsConnected)
{
string key = k.ToString();
DrawString(gfx, key, line, offset);
offset += gfx.MeasureString(key, TextFont).Width + space;
StringBuilder sb = new StringBuilder();
sb.Append(i);
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:";
float space = gfx.MeasureString(" ", TextFont).Width;
float offset = gfx.MeasureString(str, TextFont).Width + space;
DrawString(gfx, str, line);
for (int i = 0; i < (int)MouseButton.LastButton; i++)
line++;
DrawString(gfx, "Mouse:", line++);
for (int i = 0; i < 4; i++)
{
MouseButton b = (MouseButton)i;
if (mouse[b])
var state = OpenTK.Input.Mouse.GetState(i);
if (state.IsConnected)
{
string button = b.ToString();
DrawString(gfx, button, line, offset);
offset += gfx.MeasureString(button, TextFont).Width + space;
StringBuilder sb = new StringBuilder();
Vector3 pos = new Vector3(state.X, state.Y, state.WheelPrecise);
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)
{
string str = String.Format("Joystick '{0}': ", joy.Description);
DrawString(gfx, str, line);
float offset = 0;
line++;
for (int i = 0; i < joy.Axis.Count; i++)
joy_index++;
if (!String.IsNullOrEmpty(joy.Description))
{
string axis = joy.Axis[i].ToString();
DrawString(gfx, axis, line, offset);
offset += gfx.MeasureString(axis, TextFont).Width + space;
}
StringBuilder sb = new StringBuilder();
sb.Append(joy_index);
sb.Append(": '");
sb.Append(joy.Description);
sb.Append("' ");
offset = 0;
line++;
for (int i = 0; i < joy.Button.Count; i++)
{
string button = joy.Button[i].ToString();
DrawString(gfx, button, line, offset);
offset += gfx.MeasureString(button, TextFont).Width + space;
}
for (int i = 0; i < joy.Axis.Count; i++)
{
sb.Append(joy.Axis[i]);
sb.Append(" ");
}
line++;
for (int i = 0; i < joy.Button.Count; i++)
{
sb.Append(joy.Button[i]);
sb.Append(" ");
}
DrawString(gfx, sb.ToString(), line++);
}
}
return line;
}
protected override void OnUpdateFrame(FrameEventArgs e)
{;
InputDriver.Poll();
{
double clock_time = watch.Elapsed.TotalSeconds;
update_time += e.Time;
timestamp += e.Time;
update_count++;
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)
using (Graphics gfx = Graphics.FromImage(TextBitmap))
{
refresh_text = false;
using (Graphics gfx = Graphics.FromImage(TextBitmap))
int line = 0;
gfx.Clear(Color.Black);
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++);
// GameWindow information
line++;
DrawString(gfx, "GameWindow:", line++);
DrawString(gfx, String.Format("[1 - 4]:[5 - 7]: WindowState.{0}:WindowBorder.{1}",
this.WindowState, this.WindowBorder), line++);
DrawString(gfx, String.Format("[V]: VSync.{0}.", VSync), line++);
DrawString(gfx, String.Format("Bounds: {0}", Bounds), line++);
DrawString(gfx, String.Format("ClientRectangle: {0}", ClientRectangle), line++);
DrawString(gfx, String.Format("Mouse {0} and {1}. {2}.",
mouse_in_window ? "inside" : "outside",
CursorVisible ? "visible" : "hidden",
Focused ? "Focused" : "Not focused"), line++);
DrawString(gfx, String.Format("Mouse coordinates: {0}", new Vector3(Mouse.X, Mouse.Y, Mouse.WheelPrecise)), 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)
{
int line = 0;
timestamp -= 1;
update_fps = update_count;
render_fps = render_count;
update_count = 0;
render_count = 0;
gfx.Clear(Color.Black);
gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
DrawString(gfx, Context.GraphicsMode.ToString(), line++);
}
DrawString(gfx, String.Format("[1 - 4]: change WindowState (current: {0}).", this.WindowState), line++);
DrawString(gfx, String.Format("[5 - 7]: change WindowBorder (current: {0}).", this.WindowBorder), line++);
DrawString(gfx, String.Format("Focused: {0}.", this.Focused), line++);
DrawString(gfx, String.Format("Mouse {0} window.", mouse_in_window ? "inside" : "outside of"), line++);
DrawString(gfx, String.Format("Mouse visible: {0}", CursorVisible), line++);
DrawString(gfx, String.Format("Mouse position (absolute): {0}", new Vector3(Mouse.X, Mouse.Y, Mouse.Wheel)), line++);
DrawString(gfx, String.Format("Mouse position (relative): {0}", new Vector3(mouse.X, mouse.Y, mouse.WheelPrecise)), line++);
DrawString(gfx, String.Format("Window.Bounds: {0}", Bounds), line++);
DrawString(gfx, String.Format("Window.Location: {0}, Size: {1}", Location, Size), line++);
DrawString(gfx, String.Format("Window: {{X={0},Y={1},Width={2},Height={3}}}", X, Y, Width, Height), line++);
DrawString(gfx, String.Format("Window.ClientRectangle: {0}", ClientRectangle), line++);
DrawString(gfx, TypedText.ToString(), line++);
DrawKeyboard(gfx, keyboard, line++);
DrawMouse(gfx, mouse, line++);
DrawJoysticks(gfx, Joysticks, line++);
// 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(
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);
line++;
DrawString(gfx, "Joystick:", line++);
for (int i = 0; i < 4; i++)
{
JoystickCapabilities caps = Joystick.GetCapabilities(i);
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)
{
base.OnLoad(e);
watch.Start();
GL.ClearColor(Color.MidnightBlue);
GL.Enable(EnableCap.Texture2D);
@ -274,31 +363,96 @@ namespace Examples.Tests
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
if (viewport_changed)
{
viewport_changed = false;
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);
}
render_time += e.Time;
render_count++;
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.Begin(BeginMode.Quads);
if (viewport_changed)
{
viewport_changed = false;
GL.Viewport(0, 0, Width, Height);
}
DrawText();
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(1, 0); GL.Vertex2(TextBitmap.Width, 0);
GL.TexCoord2(1, 1); GL.Vertex2(TextBitmap.Width, TextBitmap.Height);
GL.TexCoord2(0, 1); GL.Vertex2(0, TextBitmap.Height);
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()

View file

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

View file

@ -9,6 +9,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;
@ -262,7 +263,13 @@ namespace OpenTK.Audio.OpenAL
/// <returns>A string containing the name of the Device.</returns>
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>
@ -277,26 +284,54 @@ namespace OpenTK.Audio.OpenAL
public static IList<string> GetString(IntPtr device, AlcGetStringList param)
{
List<string> result = new List<string>();
IntPtr t = GetStringPrivate(IntPtr.Zero, (AlcGetString)param);
System.Text.StringBuilder sb = new System.Text.StringBuilder();
byte b;
int offset = 0;
do
{
b = Marshal.ReadByte(t, offset++);
if (b != 0)
sb.Append((char)b);
if (b == 0)
{
result.Add(sb.ToString());
if (Marshal.ReadByte(t, offset) == 0) // offset already properly increased through ++
break; // 2x null
else
sb.Remove(0, sb.Length); // 1x null
}
} while (true);
return (IList<string>)result;
// 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();
byte b;
int offset = 0;
do
{
b = Marshal.ReadByte(t, offset++);
if (b != 0)
{
sb.Append((char)b);
}
else
{
// One string from the array is complete
result.Add(sb.ToString());
// 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()]

View file

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

View file

@ -1,4 +1,4 @@
#region License
#region License
//
// The Open Toolkit Library License
//
@ -75,7 +75,9 @@ namespace OpenTK
{
#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;
@ -83,12 +85,19 @@ namespace OpenTK
double update_period, 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;
Stopwatch update_watch = new Stopwatch(), render_watch = new Stopwatch();
double next_render = 0.0, next_update = 0.0;
FrameEventArgs update_args = new FrameEventArgs();
FrameEventArgs render_args = new FrameEventArgs();
@ -404,8 +413,7 @@ namespace OpenTK
//Resize += DispatchUpdateAndRenderFrame;
Debug.Print("Entering main loop.");
update_watch.Start();
render_watch.Start();
watch.Start();
while (true)
{
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)
{
RaiseUpdateFrame(update_watch, ref next_update, update_args);
RaiseRenderFrame(render_watch, ref next_render, render_args);
}
int is_running_slowly_retries = 4;
double timestamp = watch.Elapsed.TotalSeconds;
double elapsed = 0;
void RaiseUpdateFrame(Stopwatch update_watch, ref double next_update, FrameEventArgs update_args)
{
int num_updates = 0;
double total_update_time = 0;
// Cap the maximum time drift to 1 second (e.g. when the process is suspended).
double time = update_watch.Elapsed.TotalSeconds;
if (time <= 0)
elapsed = ClampElapsed(timestamp - update_timestamp);
while (elapsed > 0 && elapsed + update_epsilon >= TargetUpdatePeriod)
{
// Protect against negative Stopwatch.Elapsed values.
// See http://connect.microsoft.com/VisualStudio/feedback/details/94083/stopwatch-returns-negative-elapsed-time
update_watch.Reset();
update_watch.Start();
return;
}
if (time > 1.0)
time = 1.0;
RaiseUpdateFrame(elapsed, ref timestamp);
// Calculate difference (positive or negative) between
// actual elapsed time and target elapsed time. We must
// compensate for this difference.
update_epsilon += elapsed - TargetUpdatePeriod;
// 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();
// Prepare for next loop
elapsed = ClampElapsed(timestamp - update_timestamp);
// 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;
}
// Calculate statistics
if (num_updates > 0)
{
update_period = total_update_time / (double)num_updates;
}
}
void RaiseRenderFrame(Stopwatch render_watch, ref double next_render, FrameEventArgs render_args)
{
// Cap the maximum time drift to 1 second (e.g. when the process is suspended).
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)
{
// Schedule next render event. The 1 second cap ensures
// the process does not appear to hang.
next_render = time_left + TargetRenderPeriod;
if (next_render < -1.0)
next_render = -1.0;
render_watch.Reset();
render_watch.Start();
if (time > 0)
if (TargetUpdatePeriod <= Double.Epsilon)
{
render_period = render_args.Time = time;
OnRenderFrameInternal(render_args);
render_time = render_watch.Elapsed.TotalSeconds;
// According to the TargetUpdatePeriod documentation,
// a TargetUpdatePeriod of zero means we will raise
// UpdateFrame events as fast as possible (one event
// per ProcessEvents() call)
break;
}
is_running_slowly = update_epsilon >= TargetUpdatePeriod;
if (is_running_slowly && --is_running_slowly_retries == 0)
{
// If UpdateFrame consistently takes longer than TargetUpdateFrame
// stop raising events to avoid hanging inside the UpdateFrame loop.
break;
}
}
elapsed = ClampElapsed(timestamp - render_timestamp);
if (elapsed > 0 && elapsed >= TargetRenderPeriod)
{
RaiseRenderFrame(elapsed, ref timestamp);
}
}
void RaiseUpdateFrame(double elapsed, ref double timestamp)
{
// Raise UpdateFrame event
update_args.Time = elapsed;
OnUpdateFrameInternal(update_args);
// Update UpdatePeriod/UpdateFrequency properties
update_period = elapsed;
// Update UpdateTime property
update_timestamp = timestamp;
timestamp = watch.Elapsed.TotalSeconds;
update_time = timestamp - update_timestamp;
}
void RaiseRenderFrame(double elapsed, ref double timestamp)
{
// Raise RenderFrame event
render_args.Time = elapsed;
OnRenderFrameInternal(render_args);
// Update RenderPeriod/UpdateFrequency properties
render_period = elapsed;
// Update RenderTime property
render_timestamp = timestamp;
timestamp = watch.Elapsed.TotalSeconds;
render_time = timestamp - render_timestamp;
}
#endregion
@ -692,7 +683,7 @@ namespace OpenTK
/// </summary>
/// <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>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>
public double TargetRenderFrequency
{
@ -710,11 +701,11 @@ namespace OpenTK
{
TargetRenderPeriod = 0.0;
}
else if (value <= 200.0)
else if (value <= MaxFrequency)
{
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>
/// <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>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>
public double TargetRenderPeriod
{
@ -739,7 +730,7 @@ namespace OpenTK
set
{
EnsureUndisposed();
if (value <= 0.005)
if (value <= 1 / MaxFrequency)
{
target_render_period = 0.0;
}
@ -760,7 +751,7 @@ namespace OpenTK
/// </summary>
/// <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>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>
public double TargetUpdateFrequency
{
@ -778,11 +769,11 @@ namespace OpenTK
{
TargetUpdatePeriod = 0.0;
}
else if (value <= 200.0)
else if (value <= MaxFrequency)
{
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>
/// <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>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>
public double TargetUpdatePeriod
{
@ -807,7 +798,7 @@ namespace OpenTK
set
{
EnsureUndisposed();
if (value <= 0.005)
if (value <= 1 / MaxFrequency)
{
target_update_period = 0.0;
}
@ -815,7 +806,7 @@ namespace OpenTK
{
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.");
}
}
@ -1101,4 +1092,4 @@ namespace OpenTK
}
#endregion
}
}

View file

@ -39,14 +39,13 @@ namespace OpenTK.Graphics
/// </summary>
public sealed class GraphicsContext : IGraphicsContext, IGraphicsContextInternal
{
public delegate IntPtr GetAddressDelegate(string function);
public delegate ContextHandle GetCurrentContextDelegate();
#region --- Fields ---
IGraphicsContext implementation; // The actual render context implementation for the underlying platform.
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;
// Cache for the context handle. We need this for RemoveContext()
// in case the user does not call Dispose(). When this happens,
@ -67,17 +66,6 @@ namespace OpenTK.Graphics
#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>
/// Constructs a new GraphicsContext with the specified GraphicsMode and attaches it to the specified window.
/// </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>
/// Constructs a new GraphicsContext from a pre-existing context created outside of OpenTK.
/// </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="window">The window this context is bound to. This must be a valid window obtained through Utilities.CreateWindowInfo.</param>
/// <exception cref="GraphicsContextException">Occurs if handle is identical to a context already registered with OpenTK.</exception>
/// <param name="window">This parameter is reserved.</param>
public GraphicsContext(ContextHandle handle, IWindowInfo window)
: 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.
/// </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="window">The window this context is bound to. This must be a valid window obtained through Utilities.CreateWindowInfo.</param>
/// <param name="shareContext">A different context that shares resources with this instance, if any.
/// Pass null if the context is not shared or if this is the first GraphicsContext instruct you construct.</param>
/// <param name="major">The major version of the context (e.g. "2" for "2.1").</param>
/// <param name="minor">The minor version of the context (e.g. "1" for "2.1").</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>
/// <param name="window">This parameter is reserved.</param>
/// <param name="shareContext">This parameter is reserved.</param>
/// <param name="major">This parameter is reserved.</param>
/// <param name="minor">This parameter is reserved.</param>
/// <param name="flags">This parameter is reserved..</param>
public GraphicsContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, int major, int minor, GraphicsContextFlags flags)
: this(handle)
{
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();
}
}
: this(handle, Platform.Utilities.CreateGetAddress(), Factory.Default.CreateGetCurrentGraphicsContext())
{ }
#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>This method requires that a context is current on the calling thread.</para>
/// </remarks>
[Obsolete("Use GraphicsContext(ContextHandle, IWindowInfo) constructor instead")]
public static GraphicsContext CreateDummyContext()
{
ContextHandle handle = GetCurrentContext();
@ -326,12 +342,13 @@ namespace OpenTK.Graphics
/// <remarks>
/// <para>Instances created by this method will not be functional. Instance methods will have no effect.</para>
/// </remarks>
[Obsolete("Use GraphicsContext(ContextHandle, IWindowInfo) constructor instead")]
public static GraphicsContext CreateDummyContext(ContextHandle handle)
{
if (handle == ContextHandle.Zero)
throw new ArgumentOutOfRangeException("handle");
return new GraphicsContext(handle);
return new GraphicsContext(handle, (IWindowInfo)null);
}
#endregion
@ -352,7 +369,6 @@ namespace OpenTK.Graphics
#region public static IGraphicsContext CurrentContext
internal delegate ContextHandle GetCurrentContextDelegate();
internal static GetCurrentContextDelegate GetCurrentContext;
/// <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()
{
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,46 +1,96 @@
#region License
//
// The Open Toolkit Library License
//
// Copyright (c) 2006 - 2009 the Open Toolkit library.
//
// 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
#region License
//
// GamePad.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>
/// 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>
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.
using System;
namespace OpenTK
namespace OpenTK.Input
{
public enum GamePadAxis
{
/// <summary>The first axis of the gamepad.</summary>
Axis0 = 0,
/// <summary>The second axis of the gamepad.</summary>
Axis1,
/// <summary>The third axis of the gamepad.</summary>
Axis2,
/// <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
}
[Flags]
internal enum GamePadAxes : byte
{
LeftX = 1 << 0,
LeftY = 1 << 1,
LeftTrigger = 1 << 2,
RightX = 1 << 3,
RightY = 1 << 4,
RightTrigger = 1 << 5,
}
}

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
{
/// <summary>
/// Encapsulates the state of a GamePad device.
/// Describes the current state of a <see cref="GamePad"/> device.
/// </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
{
/// <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);
GamePadCapabilities GetCapabilities(int index);
/// <summary>
/// Retrieves the device name for the gamepad device.
/// </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>
/// <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; }
IKeyboardDriver2 KeyboardDriver { 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
// 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)
: base(id, axes, buttons)
{ }
internal TDetail Details;
internal TDetail Details = new TDetail();
}
#endregion
@ -271,49 +272,6 @@ namespace OpenTK.Input
#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
/// <summary>
@ -376,37 +334,6 @@ namespace OpenTK.Input
#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
/// <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)
{
// limit scancode to 8bits, otherwise the assignment
// below will crash randomly
scancode &= 0xff;
keys[(int)key] = scancodes[scancode] = state;
if (state && KeyDown != null)

View file

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

View file

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

View file

@ -288,6 +288,46 @@ namespace OpenTK
#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
}
}

View file

@ -133,10 +133,19 @@
</Compile>
<Compile Include="DisplayIndex.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\IInputDriver2.cs" />
<Compile Include="Input\IJoystickDriver2.cs" />
<Compile Include="Input\IKeyboardDriver2.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="Math\Matrix2.cs" />
<Compile Include="Math\Matrix2d.cs" />
@ -154,7 +163,9 @@
<Compile Include="Math\Matrix4x3.cs" />
<Compile Include="Math\Matrix4x3d.cs" />
<Compile Include="Platform\DisplayDeviceBase.cs" />
<Compile Include="Platform\MappedGamePadDriver.cs" />
<Compile Include="Platform\Windows\WinInputBase.cs" />
<Compile Include="Platform\Windows\XInputJoystick.cs" />
<Compile Include="ToolkitOptions.cs" />
<Compile Include="WindowBorder.cs">
<SubType>Code</SubType>
@ -738,8 +749,7 @@
</EmbeddedResource>
<Compile Include="Platform\MacOS\HIDInput.cs" />
<Compile Include="IntPtrEqualityComparer.cs" />
<Compile Include="Input\GamePadButton.cs" />
<Compile Include="Input\GamePadAxis.cs" />
<Compile Include="Input\GamePadAxes.cs" />
<Compile Include="Platform\SDL2\Sdl2DisplayDeviceDriver.cs" />
<Compile Include="Platform\SDL2\Sdl2Factory.cs" />
<Compile Include="Platform\SDL2\Sdl2GraphicsContext.cs" />
@ -777,6 +787,17 @@
<Compile Include="SlotAttribute.cs" />
<Compile Include="RewrittenAttribute.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>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>

View file

@ -22,7 +22,8 @@ namespace OpenTK.Platform.Dummy
/// </summary>
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;
int swap_interval;
static int handle_count;
@ -31,13 +32,20 @@ namespace OpenTK.Platform.Dummy
#region --- Constructors ---
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()
{
Handle = handle;
if (handle != ContextHandle.Zero)
{
Handle = handle;
}
Loader = loader;
Mode = new GraphicsMode(new IntPtr(2), 32, 16, 0, 0, 0, 2, false);
}
@ -45,15 +53,6 @@ namespace OpenTK.Platform.Dummy
#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 MakeCurrent(IWindowInfo info)
@ -81,9 +80,15 @@ namespace OpenTK.Platform.Dummy
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
{
@ -101,7 +106,14 @@ namespace OpenTK.Platform.Dummy
{ }
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

View file

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

View file

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

View file

@ -464,64 +464,16 @@ namespace OpenTK.Platform.MacOS
#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)
{
// 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 = IntPtr.Zero;
if (NSIsSymbolNameDefined(ptr))
{
symbol = NSLookupAndBindSymbol(ptr);
if (symbol != IntPtr.Zero)
symbol = NSAddressOfSymbol(symbol);
}
return symbol;
}
finally
{
Marshal.FreeHGlobal(ptr);
}
return NS.GetAddress(function);
}
public override IntPtr GetAddress(IntPtr function)
{
if (!NSIsSymbolNameDefined(function))
return IntPtr.Zero;
IntPtr symbol = NSLookupAndBindSymbol(function);
if (symbol != IntPtr.Zero)
symbol = NSAddressOfSymbol(symbol);
return symbol;
return NS.GetAddress(function);
}
#endregion
}
}

View file

@ -64,6 +64,11 @@ namespace OpenTK.Platform.MacOS.Carbon
short bottom;
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)
{
top = _top;
@ -236,6 +241,7 @@ namespace OpenTK.Platform.MacOS.Carbon
WindowClickProxyIconRgn = 38,
WindowClose = 72,
WindowClosed = 73,
WindowPaint = 1013,
}
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)*/
NoUpdates = (1u << 16), /* this window receives no update 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),
InWindowMenu = (1u << 27),
LiveResize = (1u << 28),
@ -530,6 +564,9 @@ namespace OpenTK.Platform.MacOS.Carbon
return retval;
}
[DllImport(carbon)]
internal static extern OSStatus SetWindowBounds(IntPtr Windows, WindowRegionCode WindowRegionCode, ref Rect globalBounds);
//[DllImport(carbon)]
//internal static extern void MoveWindow(IntPtr window, short hGlobal, short vGlobal, bool front);
@ -1006,6 +1043,12 @@ namespace OpenTK.Platform.MacOS.Carbon
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 ---
[DllImport(carbon)]

View file

@ -32,7 +32,10 @@ using System.Text;
namespace OpenTK.Platform.MacOS.Carbon
{
using CFIndex = System.IntPtr;
using CFRunLoop = System.IntPtr;
using CFStringRef = System.IntPtr;
using CFTypeRef = System.IntPtr;
struct CFArray
{
@ -59,6 +62,7 @@ namespace OpenTK.Platform.MacOS.Carbon
}
}
}
struct CFDictionary
{
public CFDictionary(IntPtr reference)
@ -76,18 +80,16 @@ namespace OpenTK.Platform.MacOS.Carbon
return CF.CFDictionaryGetCount(dictionaryRef);
}
}
public double GetNumberValue(string key)
{
unsafe
{
double retval;
IntPtr cfnum = CF.CFDictionaryGetValue(dictionaryRef,
CF.CFSTR(key));
double retval;
IntPtr cfnum = CF.CFDictionaryGetValue(dictionaryRef,
CF.CFSTR(key));
CF.CFNumberGetValue(cfnum, CF.CFNumberType.kCFNumberDoubleType, &retval);
CF.CFNumberGetValue(cfnum, CF.CFNumberType.kCFNumberDoubleType, out retval);
return retval;
}
return retval;
}
}
class CF
@ -106,6 +108,9 @@ namespace OpenTK.Platform.MacOS.Carbon
[DllImport(appServices)]
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.
// 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.
@ -117,9 +122,38 @@ namespace OpenTK.Platform.MacOS.Carbon
}
[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)]
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
{
@ -150,6 +184,25 @@ namespace OpenTK.Platform.MacOS.Carbon
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");
[DllImport(appServices)]

View file

@ -74,6 +74,8 @@ namespace OpenTK.Platform.MacOS
new Dictionary<IntPtr, WeakReference>(new IntPtrEqualityComparer());
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 mIsActive = false;
@ -106,18 +108,6 @@ namespace OpenTK.Platform.MacOS
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,
GraphicsMode mode, GameWindowFlags options, DisplayDevice device)
{
@ -382,7 +372,14 @@ namespace OpenTK.Platform.MacOS
case KeyboardEventKind.RawKeyDown:
Keymap.TryGetValue(code, out key);
// Legacy keyboard API
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))
{
OnKeyPress(mKeyPressArgs);
@ -391,7 +388,12 @@ namespace OpenTK.Platform.MacOS
case KeyboardEventKind.RawKeyUp:
Keymap.TryGetValue(code, out key);
// Legacy keyboard API
InputDriver.Keyboard[0].SetKey(key, (uint)code, false);
// Raise KeyUp for new keyboard API
mKeyUpArgs.Key = key;
KeyUp(this, mKeyUpArgs);
return OSStatus.NoError;
case KeyboardEventKind.RawKeyModifiersChanged:
@ -640,52 +642,51 @@ namespace OpenTK.Platform.MacOS
}
Rect GetRegion()
Rect GetClientSize()
{
Rect retval = API.GetWindowBounds(window.Handle, WindowRegionCode.ContentRegion);
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)
{
if (WindowState == WindowState.Fullscreen)
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()
{
if (WindowState == WindowState.Fullscreen)
return;
Rect r = API.GetWindowBounds(window.Handle, WindowRegionCode.StructureRegion);
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);
}
@ -871,13 +872,13 @@ namespace OpenTK.Platform.MacOS
public int X
{
get { return ClientRectangle.X; }
get { return Bounds.X; }
set { Location = new Point(value, Y); }
}
public int Y
{
get { return ClientRectangle.Y; }
get { return Bounds.Y; }
set { Location = new Point(X, value); }
}

View file

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

View file

@ -35,6 +35,7 @@ namespace OpenTK.Platform.MacOS
{
using Carbon;
using CFAllocatorRef = System.IntPtr;
using CFArrayRef = System.IntPtr;
using CFDictionaryRef = System.IntPtr;
using CFIndex = System.IntPtr;
using CFRunLoop = System.IntPtr;
@ -50,25 +51,53 @@ namespace OpenTK.Platform.MacOS
// Requires Mac OS X 10.5 or higher.
// 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
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 Dictionary<IntPtr, MouseState> MouseDevices =
new Dictionary<IntPtr, MouseState>(new IntPtrEqualityComparer());
readonly Dictionary<IntPtr, MouseData> MouseDevices =
new Dictionary<IntPtr, MouseData>(new IntPtrEqualityComparer());
readonly Dictionary<int, IntPtr> MouseIndexToDevice =
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 =
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 CFString InputLoopMode = CF.RunLoopModeDefault;
readonly CFDictionary DeviceTypes = new CFDictionary();
readonly MappedGamePadDriver mapped_gamepad = new MappedGamePadDriver();
NativeMethods.IOHIDDeviceCallback HandleDeviceAdded;
NativeMethods.IOHIDDeviceCallback HandleDeviceRemoved;
NativeMethods.IOHIDValueCallback HandleDeviceValueReceived;
@ -93,7 +122,7 @@ namespace OpenTK.Platform.MacOS
#endregion
#region Private Members
#region Private Members
IOHIDManagerRef CreateHIDManager()
{
@ -119,110 +148,158 @@ namespace OpenTK.Platform.MacOS
void DeviceAdded(IntPtr context, IOReturn res, IntPtr sender, IOHIDDeviceRef device)
{
if (NativeMethods.IOHIDDeviceOpen(device, IOOptionBits.Zero) == IOReturn.Zero)
try
{
if (NativeMethods.IOHIDDeviceConformsTo(device,
HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse))
bool recognized = false;
if (NativeMethods.IOHIDDeviceOpen(device, IOOptionBits.Zero) == IOReturn.Zero)
{
if (!MouseDevices.ContainsKey(device))
if (NativeMethods.IOHIDDeviceConformsTo(device,
HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse))
{
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);
AddMouse(sender, device);
recognized = true;
}
else
if (NativeMethods.IOHIDDeviceConformsTo(device,
HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard))
{
Debug.Print("Mouse device {0:x} reconnected, sender is {1:x}", device, sender);
MouseState state = MouseDevices[device];
state.IsConnected = true;
MouseDevices[device] = state;
AddKeyboard(sender, device);
recognized = true;
}
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 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
NativeMethods.IOHIDDeviceRegisterInputValueCallback(device,
HandleDeviceValueReceived, device);
NativeMethods.IOHIDDeviceScheduleWithRunLoop(device, RunLoop, InputLoopMode);
}
}
if (NativeMethods.IOHIDDeviceConformsTo(device,
HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard))
{
if (!KeyboardDevices.ContainsKey(device))
{
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;
}
}
// 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.
// Thanks to Jase: http://www.opentk.com/node/2800
NativeMethods.IOHIDDeviceRegisterInputValueCallback(device,
HandleDeviceValueReceived, device);
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)
{
if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse) &&
MouseDevices.ContainsKey(device))
try
{
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
MouseState state = MouseDevices[device];
state.IsConnected = false;
MouseDevices[device] = state;
if (MouseDevices.ContainsKey(device))
{
RemoveMouse(sender, device);
recognized = true;
}
if (KeyboardDevices.ContainsKey(device))
{
RemoveKeyboard(sender, device);
recognized = true;
}
if (JoystickDevices.ContainsKey(device))
{
RemoveJoystick(sender, device);
recognized = true;
}
if (recognized)
{
NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, IntPtr.Zero, IntPtr.Zero);
NativeMethods.IOHIDDeviceUnscheduleFromRunLoop(device, RunLoop, InputLoopMode);
}
}
if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard) &&
KeyboardDevices.ContainsKey(device))
catch (Exception e)
{
Debug.Print("Keyboard device {0:x} disconnected, sender is {1:x}", device, sender);
// Keep the device in case it comes back later on
KeyboardState state = KeyboardDevices[device];
state.IsConnected = false;
KeyboardDevices[device] = state;
Debug.Print("[Mac] Exception in managed callback: {0}", e);
}
NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, IntPtr.Zero, IntPtr.Zero);
NativeMethods.IOHIDDeviceUnscheduleWithRunLoop(device, RunLoop, InputLoopMode);
}
void DeviceValueReceived(IntPtr context, IOReturn res, IntPtr sender, IOHIDValueRef val)
{
if (disposed)
try
{
Debug.Print("DeviceValueReceived({0}, {1}, {2}, {3}) called on disposed {4}",
context, res, sender, val, GetType());
return;
}
if (disposed)
{
Debug.Print("DeviceValueReceived({0}, {1}, {2}, {3}) called on disposed {4}",
context, res, sender, val, GetType());
return;
}
MouseState mouse;
KeyboardState keyboard;
if (MouseDevices.TryGetValue(context, out mouse))
{
MouseDevices[context] = UpdateMouse(mouse, val);
MouseData mouse;
KeyboardData keyboard;
JoystickData joystick;
if (MouseDevices.TryGetValue(context, out mouse))
{
UpdateMouse(mouse, val);
}
else if (KeyboardDevices.TryGetValue(context, out keyboard))
{
UpdateKeyboard(keyboard, val);
}
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);
}
}
else if (KeyboardDevices.TryGetValue(context, out keyboard))
catch (Exception e)
{
KeyboardDevices[context] = UpdateKeyboard(keyboard, val);
}else{
//Debug.Print ("Device {0:x} not found in list of keyboards or mice", sender);
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);
int v_int = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32();
@ -237,28 +314,52 @@ namespace OpenTK.Platform.MacOS
switch ((HIDUsageGD)usage)
{
case HIDUsageGD.X:
state.X += v_int;
mouse.State.X += v_int;
break;
case HIDUsageGD.Y:
state.Y += v_int;
mouse.State.Y += v_int;
break;
case HIDUsageGD.Wheel:
state.WheelPrecise += v_int;
mouse.State.WheelPrecise += v_int;
break;
}
break;
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;
}
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);
int v_int = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32();
@ -267,31 +368,357 @@ namespace OpenTK.Platform.MacOS
// 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.
if(usage < 0 ) return state;
if (usage >= 0)
{
switch (page)
{
case HIDPage.GenericDesktop:
case HIDPage.KeyboardOrKeypad:
if (usage >= RawKeyMap.Length)
{
Debug.Print("[Warning] Key {0} not mapped.", usage);
}
keyboard.State.SetKeyState(RawKeyMap[usage], (byte)usage, v_int != 0);
break;
}
}
}
#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:
case HIDPage.KeyboardOrKeypad:
if (usage >= RawKeyMap.Length)
switch ((HIDUsageGD)usage)
{
Debug.Print("[Warning] Key {0} not mapped.", usage);
return state;
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);
}
}
state.SetKeyState(RawKeyMap[usage], (byte)usage, v_int != 0);
break;
}
return state;
}
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
#region IInputDriver2 Members
public IMouseDriver2 MouseDriver { 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
@ -300,9 +727,9 @@ namespace OpenTK.Platform.MacOS
MouseState IMouseDriver2.GetState()
{
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;
@ -313,7 +740,7 @@ namespace OpenTK.Platform.MacOS
IntPtr device;
if (MouseIndexToDevice.TryGetValue(index, out device))
{
return MouseDevices[device];
return MouseDevices[device].State;
}
return new MouseState();
@ -332,9 +759,9 @@ namespace OpenTK.Platform.MacOS
KeyboardState IKeyboardDriver2.GetState()
{
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;
@ -345,7 +772,7 @@ namespace OpenTK.Platform.MacOS
IntPtr device;
if (KeyboardIndexToDevice.TryGetValue(index, out device))
{
return KeyboardDevices[device];
return KeyboardDevices[device].State;
}
return new KeyboardState();
@ -366,6 +793,40 @@ namespace OpenTK.Platform.MacOS
#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
class NativeMethods
@ -418,6 +879,12 @@ namespace OpenTK.Platform.MacOS
CFRunLoop inCFRunLoop,
CFString inCFRunLoopMode);
[DllImport(hid)]
public static extern void IOHIDManagerUnscheduleFromRunLoop(
IOHIDManagerRef inIOHIDManagerRef,
CFRunLoop inCFRunLoop,
CFString inCFRunLoopMode);
[DllImport(hid)]
public static extern void IOHIDManagerSetDeviceMatching(
IOHIDManagerRef manager,
@ -444,6 +911,13 @@ namespace OpenTK.Platform.MacOS
HIDPage inUsagePage, // the usage page 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)]
public static extern void IOHIDDeviceRegisterInputValueCallback(
IOHIDDeviceRef device,
@ -463,7 +937,7 @@ namespace OpenTK.Platform.MacOS
CFString inCFRunLoopMode);
[DllImport(hid)]
public static extern void IOHIDDeviceUnscheduleWithRunLoop(
public static extern void IOHIDDeviceUnscheduleFromRunLoop(
IOHIDDeviceRef device,
CFRunLoop inCFRunLoop,
CFString inCFRunLoopMode);
@ -479,16 +953,37 @@ namespace OpenTK.Platform.MacOS
IOHIDValueRef @value,
IOHIDValueScaleType type) ;
[DllImport(hid)]
public static extern IOHIDElementType IOHIDElementGetType(
IOHIDElementRef element);
[DllImport(hid)]
public static extern int IOHIDElementGetUsage(IOHIDElementRef elem);
[DllImport(hid)]
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 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
{
Physical, // [device min, device max]
@ -593,6 +1088,65 @@ namespace OpenTK.Platform.MacOS
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
{
Button_1 = 0x01, /* (primary/trigger) */
@ -981,19 +1535,22 @@ namespace OpenTK.Platform.MacOS
hidmanager, IntPtr.Zero, IntPtr.Zero);
NativeMethods.IOHIDManagerRegisterDeviceRemovalCallback(
hidmanager, IntPtr.Zero, IntPtr.Zero);
NativeMethods.IOHIDManagerScheduleWithRunLoop(
hidmanager, IntPtr.Zero, IntPtr.Zero);
NativeMethods.IOHIDManagerUnscheduleFromRunLoop(
hidmanager, RunLoop, InputLoopMode);
foreach (var device in MouseDevices.Keys)
{
NativeMethods.IOHIDDeviceRegisterInputValueCallback(
device, IntPtr.Zero, IntPtr.Zero);
DeviceRemoved(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, device);
}
foreach (var device in KeyboardDevices.Keys)
{
NativeMethods.IOHIDDeviceRegisterInputValueCallback(
device, IntPtr.Zero, IntPtr.Zero);
DeviceRemoved(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, device);
}
foreach (var device in JoystickDevices.Keys)
{
DeviceRemoved(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, device);
}
HandleDeviceAdded = null;

View file

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

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
// OTHER DEALINGS IN THE SOFTWARE.
//
using System.Runtime.InteropServices;
#endregion
@ -96,7 +94,6 @@ namespace OpenTK.Platform.SDL2
[SuppressUnmanagedCodeSecurity]
[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 SDL_CreateWindow(string title, int x, int y, int w, int h, WindowFlags flags);
[SuppressUnmanagedCodeSecurity]
[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)]
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]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetCurrentDisplayMode", ExactSpelling = true)]
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]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetDisplayBounds", ExactSpelling = true)]
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)]
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]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickClose", ExactSpelling = true)]
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]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickGetAxis", ExactSpelling = true)]
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)]
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]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickName", ExactSpelling = true)]
static extern IntPtr JoystickNameInternal(IntPtr joystick);
@ -320,6 +419,34 @@ namespace OpenTK.Platform.SDL2
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_WarpMouseInWindow", ExactSpelling = true)]
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
{
[SuppressUnmanagedCodeSecurity]
@ -334,6 +461,7 @@ namespace OpenTK.Platform.SDL2
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetAttribute", ExactSpelling = true)]
public static extern int GetAttribute(ContextAttribute attr, out int value);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetCurrentContext", ExactSpelling = true)]
public static extern IntPtr GetCurrentContext();
@ -461,6 +589,13 @@ namespace OpenTK.Platform.SDL2
ES = 0x0004
}
enum EventState
{
Query = -1,
Ignore = 0,
Enable = 1
}
enum EventType
{
FIRSTEVENT = 0,
@ -500,6 +635,61 @@ namespace OpenTK.Platform.SDL2
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
{
UNKNOWN = 0,
@ -1037,6 +1227,17 @@ namespace OpenTK.Platform.SDL2
JOYSTICK | HAPTIC | GAMECONTROLLER
}
enum SysWMType
{
Unknown = 0,
Windows,
X11,
Wayland,
DirectFB,
Cocoa,
UIKit,
}
enum WindowEventID : byte
{
NONE,
@ -1079,6 +1280,41 @@ namespace OpenTK.Platform.SDL2
#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
{
public uint Format;
@ -1109,21 +1345,21 @@ namespace OpenTK.Platform.SDL2
public MouseWheelEvent Wheel;
[FieldOffset(0)]
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
[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)]
public QuitEvent quit;
[FieldOffset(0)]
@ -1141,6 +1377,21 @@ namespace OpenTK.Platform.SDL2
#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
{
public EventType Type;
@ -1154,6 +1405,68 @@ namespace OpenTK.Platform.SDL2
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
{
public EventType Type;
@ -1242,6 +1555,65 @@ namespace OpenTK.Platform.SDL2
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
{
public const int TextSize = 32;

View file

@ -34,7 +34,7 @@ namespace OpenTK.Platform.SDL2
{
class Sdl2Factory : IPlatformFactory
{
readonly IInputDriver2 InputDriver = new Sdl2InputDriver();
readonly Sdl2InputDriver InputDriver = new Sdl2InputDriver();
bool disposed;
/// <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)
{
return new Sdl2NativeWindow(x, y, width, height, title, options, device);
return new Sdl2NativeWindow(x, y, width, height, title, options, device, InputDriver);
}
public IDisplayDeviceDriver CreateDisplayDeviceDriver()
@ -104,6 +104,11 @@ namespace OpenTK.Platform.SDL2
return InputDriver.GamePadDriver;
}
public IJoystickDriver2 CreateJoystickDriver()
{
return InputDriver.JoystickDriver;
}
#endregion
#region IDisposable Members

View file

@ -157,6 +157,8 @@ namespace OpenTK.Platform.SDL2
int major, int minor,
GraphicsContextFlags flags)
{
ContextProfileFlags cpflags = 0;
if (mode.AccumulatorFormat.BitsPerPixel > 0)
{
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_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)
@ -223,7 +234,6 @@ namespace OpenTK.Platform.SDL2
*/
{
ContextProfileFlags cpflags = 0;
if ((flags & GraphicsContextFlags.Embedded) != 0)
{
cpflags |= ContextProfileFlags.ES;

View file

@ -55,10 +55,21 @@ namespace OpenTK.Platform.SDL2
{
lock (SDL.Sync)
{
SDL.GameControllerEventState(EventState.Enable);
SDL.JoystickEventState(EventState.Enable);
EventFilterDelegate = Marshal.GetFunctionPointerForDelegate(EventFilterDelegate_GCUnsafe);
driver_handle = new IntPtr(count++);
DriverHandles.Add(driver_handle, this);
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:
driver.mouse_driver.ProcessWheelEvent(ev.Wheel);
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
{
throw new NotImplementedException();
return joystick_driver;
}
}
public IJoystickDriver2 JoystickDriver
{
get
{
return joystick_driver;
}
}

View file

@ -32,61 +32,482 @@ using OpenTK.Input;
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 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 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;
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()
{
joysticks_readonly = joysticks.AsReadOnly();
RefreshJoysticks();
}
#region Private Members
void RefreshJoysticks()
JoystickDevice<Sdl2JoystickDetails> OpenJoystick(int id)
{
joysticks.Clear();
JoystickDevice<Sdl2JoystickDetails> joystick = null;
int num_axes = 0;
int num_buttons = 0;
int num_hats = 0;
int num_balls = 0;
int count = SDL.NumJoysticks();
for (int i = 0; i < count; i++)
IntPtr handle = SDL.JoystickOpen(id);
if (handle != IntPtr.Zero)
{
JoystickDevice<Sdl2JoystickDetails> joystick = null;
int num_axes = 0;
int num_buttons = 0;
int num_hats = 0;
int num_balls = 0;
num_axes = SDL.JoystickNumAxes(handle);
num_buttons = SDL.JoystickNumButtons(handle);
num_hats = SDL.JoystickNumHats(handle);
num_balls = SDL.JoystickNumBalls(handle);
IntPtr handle = SDL.JoystickOpen(i);
if (handle != IntPtr.Zero)
{
num_axes = SDL.JoystickNumAxes(handle);
num_buttons = SDL.JoystickNumButtons(handle);
num_hats = SDL.JoystickNumHats(handle);
num_balls = SDL.JoystickNumBalls(handle);
joystick = new JoystickDevice<Sdl2JoystickDetails>(id, num_axes, num_buttons);
joystick.Description = SDL.JoystickName(handle);
joystick.Details.Handle = handle;
joystick.Details.Guid = SDL.JoystickGetGUID(handle).ToGuid();
joystick.Details.HatCount = num_hats;
joystick.Details.BallCount = num_balls;
joystick = new JoystickDevice<Sdl2JoystickDetails>(i, num_axes, num_buttons);
joystick.Description = SDL.JoystickName(handle);
joystick.Details.Handle = handle;
joystick.Details.HatCount = num_hats;
joystick.Details.BallCount = num_balls;
joysticks.Add(joystick);
}
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);
}
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
#region IJoystickDriver Members
@ -101,49 +522,114 @@ namespace OpenTK.Platform.SDL2
public void Poll()
{
SDL.JoystickUpdate();
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);
}
}
// Do nothing
}
#endregion
#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)
{
if (joysticks.Count >= index)
if (IsControllerValid(index))
{
return controllers[index].State;
}
return new GamePadState();
}
public string GetDeviceName(int index)
public string GetName(int index)
{
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

View file

@ -70,7 +70,7 @@ namespace OpenTK.Platform.SDL2
// Argument for KeyDown and KeyUp events (allocated once to avoid runtime allocations)
readonly KeyboardKeyEventArgs key_args = new KeyboardKeyEventArgs();
readonly IInputDriver input_driver = new Sdl2InputDriver();
readonly IInputDriver input_driver;
readonly EventFilter EventFilterDelegate_GCUnsafe = FilterEvents;
readonly IntPtr EventFilterDelegate;
@ -81,10 +81,13 @@ namespace OpenTK.Platform.SDL2
static readonly Sdl2KeyMap map = new Sdl2KeyMap();
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)
{
this.input_driver = input_driver;
var bounds = device.Bounds;
var flags = TranslateFlags(options);
flags |= WindowFlags.OPENGL;

View file

@ -170,6 +170,32 @@ namespace OpenTK.Platform
#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 ---
/// <summary>
@ -275,7 +301,7 @@ namespace OpenTK.Platform
public static IWindowInfo CreateSdl2WindowInfo(IntPtr windowHandle)
{
return new OpenTK.Platform.SDL2.Sdl2WindowInfo(
SDL2.SDL.CreateWindowFrom(windowHandle), null);
windowHandle, null);
}
#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);
[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
@ -241,9 +246,7 @@ namespace OpenTK.Platform.Windows
#region CallWindowProc
#if RELEASE
[SuppressUnmanagedCodeSecurity]
#endif
[DllImport("user32.dll", SetLastError = true)]
internal static extern LRESULT CallWindowProc(WNDPROC lpPrevWndFunc, HWND hWnd, WindowMessage Msg,
WPARAM wParam, LPARAM lParam);
@ -264,9 +267,9 @@ namespace OpenTK.Platform.Windows
SetLastError(0);
if (IntPtr.Size == 4)
retval = new IntPtr(SetWindowLong(handle, item, newValue.ToInt32()));
retval = new IntPtr(SetWindowLongInternal(handle, item, newValue.ToInt32()));
else
retval = SetWindowLongPtr(handle, item, newValue);
retval = SetWindowLongPtrInternal(handle, item, newValue);
if (retval == IntPtr.Zero)
{
@ -283,30 +286,22 @@ namespace OpenTK.Platform.Windows
return SetWindowLong(handle, GetWindowLongOffsets.WNDPROC, Marshal.GetFunctionPointerForDelegate(newValue));
}
#if RELASE
[SuppressUnmanagedCodeSecurity]
#endif
[DllImport("user32.dll", SetLastError = true)]
static extern LONG SetWindowLong(HWND hWnd, GetWindowLongOffsets nIndex, LONG dwNewLong);
[DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLong")]
static extern LONG SetWindowLongInternal(HWND hWnd, GetWindowLongOffsets nIndex, LONG dwNewLong);
#if RELASE
[SuppressUnmanagedCodeSecurity]
#endif
[DllImport("user32.dll", SetLastError = true)]
static extern LONG_PTR SetWindowLongPtr(HWND hWnd, GetWindowLongOffsets nIndex, LONG_PTR dwNewLong);
[DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLongPtr")]
static extern LONG_PTR SetWindowLongPtrInternal(HWND hWnd, GetWindowLongOffsets nIndex, LONG_PTR dwNewLong);
#if RELASE
[SuppressUnmanagedCodeSecurity]
#endif
[DllImport("user32.dll", SetLastError = true)]
static extern LONG SetWindowLong(HWND hWnd, GetWindowLongOffsets nIndex,
[DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLong")]
static extern LONG SetWindowLongInternal(HWND hWnd, GetWindowLongOffsets nIndex,
[MarshalAs(UnmanagedType.FunctionPtr)]WindowProcedure dwNewLong);
#if RELASE
[SuppressUnmanagedCodeSecurity]
#endif
[DllImport("user32.dll", SetLastError = true)]
static extern LONG_PTR SetWindowLongPtr(HWND hWnd, GetWindowLongOffsets nIndex,
[DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLongPtr")]
static extern LONG_PTR SetWindowLongPtrInternal(HWND hWnd, GetWindowLongOffsets nIndex,
[MarshalAs(UnmanagedType.FunctionPtr)]WindowProcedure dwNewLong);
#endregion
@ -407,9 +402,7 @@ namespace OpenTK.Platform.Windows
#region DispatchMessage
#if RELEASE
[System.Security.SuppressUnmanagedCodeSecurity]
#endif
[SuppressUnmanagedCodeSecurity]
[DllImport("User32.dll"), CLSCompliant(false)]
internal static extern LRESULT DispatchMessage(ref MSG msg);
@ -417,9 +410,7 @@ namespace OpenTK.Platform.Windows
#region TranslateMessage
#if RELEASE
[System.Security.SuppressUnmanagedCodeSecurity]
#endif
[SuppressUnmanagedCodeSecurity]
[DllImport("User32.dll"), CLSCompliant(false)]
internal static extern BOOL TranslateMessage(ref MSG lpMsg);
@ -3752,7 +3743,7 @@ namespace OpenTK.Platform.Windows
#region WindowMessage
internal enum WindowMessage : uint
internal enum WindowMessage : int
{
NULL = 0x0000,
CREATE = 0x0001,

View file

@ -202,14 +202,14 @@ namespace OpenTK.Platform.Windows
}
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
{
fixed (int* piAttribIList_ptr = piAttribIList)
fixed (Single* pfAttribFList_ptr = pfAttribFList)
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);
}

View file

@ -8,6 +8,7 @@
#endregion
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Reflection;
@ -34,6 +35,9 @@ namespace OpenTK.Platform.Windows
internal const string Library = "OPENGL32.DLL";
readonly static Dictionary<string, bool> extensions =
new Dictionary<string, bool>();
private static Assembly assembly;
private static Type wglClass;
private static Type delegatesClass;
@ -121,43 +125,61 @@ namespace OpenTK.Platform.Windows
#endregion
#region public static partial class Arb
/// <summary>Contains ARB extensions for WGL.</summary>
public static partial class Arb
public static bool SupportsExtension(string name)
{
/// <summary>
/// Checks if a Wgl extension is supported by the given context.
/// </summary>
/// <param name="context">The device context.</param>
/// <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(WinGLContext context, string ext)
return SupportsExtension(Wgl.GetCurrentDC(), name);
}
/// <summary>
/// Checks if a Wgl extension is supported by the given context.
/// </summary>
/// <param name="context">The device context.</param>
/// <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(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.
// 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
// have to create one accelerated and one non-accelerated context in the same application, with the
// 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
{
extensions = new string((sbyte*)get(context.DeviceContext))
.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
str = new string((sbyte*)str_ptr);
}
if (extensions == null || extensions.Length == 0)
return false;
foreach (string s in extensions)
if (s == ext)
return true;
foreach (string ext in str.Split(' '))
{
extensions.Add(ext, true);
}
}
return false;
}
if (extensions.Count > 0)
{
return extensions.ContainsKey(name);
}
return false;
}
#region public static partial class Arb
/// <summary>Contains ARB extensions for WGL.</summary>
public static partial class Arb
{
}
#endregion
@ -167,27 +189,6 @@ namespace OpenTK.Platform.Windows
/// <summary>Contains EXT extensions for WGL.</summary>
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

View file

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

View file

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

View file

@ -240,6 +240,333 @@ namespace OpenTK.Platform.Windows
#endregion
#region Message Handlers
void HandleActivate(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
// 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.
bool new_focused_state = Focused;
if (IntPtr.Size == 4)
focused = (wParam.ToInt32() & 0xFFFF) != 0;
else
focused = (wParam.ToInt64() & 0xFFFF) != 0;
if (new_focused_state != Focused)
FocusedChanged(this, EventArgs.Empty);
}
void HandleEnterModalLoop(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
// Entering the modal size/move loop: we don't want rendering to
// stop during this time, so we register a timer callback to continue
// processing from time to time.
is_in_modal_loop = true;
StartTimer(handle);
if (!CursorVisible)
UngrabCursor();
}
void HandleExitModalLoop(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
// Exiting from Modal size/move loop: the timer callback is no longer
// necessary.
is_in_modal_loop = false;
StopTimer(handle);
// Ensure cursor remains grabbed
if (!CursorVisible)
GrabCursor();
}
void HandleWindowPositionChanged(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
unsafe
{
WindowPosition* pos = (WindowPosition*)lParam;
if (window != null && pos->hwnd == window.Handle)
{
Point new_location = new Point(pos->x, pos->y);
if (Location != new_location)
{
bounds.Location = new_location;
Move(this, EventArgs.Empty);
}
Size new_size = new Size(pos->cx, pos->cy);
if (Size != new_size)
{
bounds.Width = pos->cx;
bounds.Height = pos->cy;
Win32Rectangle rect;
Functions.GetClientRect(handle, out rect);
client_rectangle = rect.ToRectangle();
Functions.SetWindowPos(child_window.Handle, IntPtr.Zero, 0, 0, ClientRectangle.Width, ClientRectangle.Height,
SetWindowPosFlags.NOZORDER | SetWindowPosFlags.NOOWNERZORDER |
SetWindowPosFlags.NOACTIVATE | SetWindowPosFlags.NOSENDCHANGING);
if (suppress_resize <= 0)
Resize(this, EventArgs.Empty);
}
if (!is_in_modal_loop)
{
// If we are in a modal resize/move loop, cursor grabbing is
// handled inside [ENTER|EXIT]SIZEMOVE case above.
// If not, then we have to handle cursor grabbing here.
if (!CursorVisible)
GrabCursor();
}
}
}
}
void HandleStyleChanged(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
WindowBorder old_border = windowBorder;
WindowBorder new_border = old_border;
unsafe
{
GWL get_window_style = (GWL)unchecked(wParam.ToInt32());
if ((get_window_style & (GWL.STYLE | GWL.EXSTYLE)) != 0)
{
WindowStyle style = ((StyleStruct*)lParam)->New;
if ((style & WindowStyle.Popup) != 0)
new_border = WindowBorder.Hidden;
else if ((style & WindowStyle.ThickFrame) != 0)
new_border = WindowBorder.Resizable;
else if ((style & ~(WindowStyle.ThickFrame | WindowStyle.MaximizeBox)) != 0)
new_border = WindowBorder.Fixed;
}
}
if (new_border != windowBorder)
{
// Ensure cursor remains grabbed
if (!CursorVisible)
GrabCursor();
windowBorder = new_border;
WindowBorderChanged(this, EventArgs.Empty);
}
}
void HandleSize(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
SizeMessage state = (SizeMessage)wParam.ToInt64();
WindowState new_state = windowState;
switch (state)
{
case SizeMessage.RESTORED:
new_state = borderless_maximized_window_state ?
WindowState.Maximized : WindowState.Normal;
break;
case SizeMessage.MINIMIZED:
new_state = WindowState.Minimized;
break;
case SizeMessage.MAXIMIZED:
new_state = WindowBorder == WindowBorder.Hidden ?
WindowState.Fullscreen : WindowState.Maximized;
break;
}
if (new_state != windowState)
{
windowState = new_state;
WindowStateChanged(this, EventArgs.Empty);
// Ensure cursor remains grabbed
if (!CursorVisible)
GrabCursor();
}
}
void HandleChar(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
if (IntPtr.Size == 4)
key_press.KeyChar = (char)wParam.ToInt32();
else
key_press.KeyChar = (char)wParam.ToInt64();
KeyPress(this, key_press);
}
void HandleMouseMove(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Point point = new Point(
(short)((uint)lParam.ToInt32() & 0x0000FFFF),
(short)(((uint)lParam.ToInt32() & 0xFFFF0000) >> 16));
mouse.Position = point;
if (mouse_outside_window)
{
// Once we receive a mouse move event, it means that the mouse has
// re-entered the window.
mouse_outside_window = false;
EnableMouseTracking();
MouseEnter(this, EventArgs.Empty);
}
}
void HandleMouseLeave(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
mouse_outside_window = true;
// Mouse tracking is disabled automatically by the OS
MouseLeave(this, EventArgs.Empty);
}
void HandleMouseWheel(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
// This is due to inconsistent behavior of the WParam value on 64bit arch, whese
// wparam = 0xffffffffff880000 or wparam = 0x00000000ff100000
mouse.WheelPrecise += ((long)wParam << 32 >> 48) / 120.0f;
}
void HandleLButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.SetCapture(window.Handle);
mouse[MouseButton.Left] = true;
}
void HandleMButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.SetCapture(window.Handle);
mouse[MouseButton.Middle] = true;
}
void HandleRButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.SetCapture(window.Handle);
mouse[MouseButton.Right] = true;
}
void HandleXButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.SetCapture(window.Handle);
mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) !=
(int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = true;
}
void HandleLButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.ReleaseCapture();
mouse[MouseButton.Left] = false;
}
void HandleMButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.ReleaseCapture();
mouse[MouseButton.Middle] = false;
}
void HandleRButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.ReleaseCapture();
mouse[MouseButton.Right] = false;
}
void HandleXButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
Functions.ReleaseCapture();
mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) !=
(int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = false;
}
void HandleKeyboard(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
bool pressed =
message == WindowMessage.KEYDOWN ||
message == WindowMessage.SYSKEYDOWN;
// Shift/Control/Alt behave strangely when e.g. ShiftRight is held down and ShiftLeft is pressed
// and released. It looks like neither key is released in this case, or that the wrong key is
// released in the case of Control and Alt.
// To combat this, we are going to release both keys when either is released. Hacky, but should work.
// Win95 does not distinguish left/right key constants (GetAsyncKeyState returns 0).
// In this case, both keys will be reported as pressed.
bool extended = (lParam.ToInt64() & ExtendedBit) != 0;
short scancode = (short)((lParam.ToInt64() >> 16) & 0xFF);
VirtualKeys vkey = (VirtualKeys)wParam;
bool is_valid;
Key key = KeyMap.TranslateKey(scancode, vkey, extended, false, out is_valid);
if (is_valid)
{
keyboard.SetKey(key, (byte)scancode, pressed);
if (pressed)
{
key_down.Key = key;
KeyDown(this, key_down);
}
else
{
key_up.Key = key;
KeyUp(this, key_up);
}
}
}
void HandleKillFocus(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
keyboard.ClearKeys();
}
void HandleCreate(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
CreateStruct cs = (CreateStruct)Marshal.PtrToStructure(lParam, typeof(CreateStruct));
if (cs.hwndParent == IntPtr.Zero)
{
bounds.X = cs.x;
bounds.Y = cs.y;
bounds.Width = cs.cx;
bounds.Height = cs.cy;
Win32Rectangle rect;
Functions.GetClientRect(handle, out rect);
client_rectangle = rect.ToRectangle();
invisible_since_creation = true;
}
}
void HandleClose(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
System.ComponentModel.CancelEventArgs e = new System.ComponentModel.CancelEventArgs();
Closing(this, e);
if (!e.Cancel)
{
DestroyWindow();
}
}
void HandleDestroy(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
exists = false;
if (handle == window.Handle)
{
Functions.UnregisterClass(ClassName, Instance);
}
window.Dispose();
child_window.Dispose();
Closed(this, EventArgs.Empty);
}
#endregion
#region WindowProcedure
IntPtr WindowProcedure(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
@ -249,132 +576,32 @@ namespace OpenTK.Platform.Windows
#region Size / Move / Style events
case WindowMessage.ACTIVATE:
// 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.
bool new_focused_state = Focused;
if (IntPtr.Size == 4)
focused = (wParam.ToInt32() & 0xFFFF) != 0;
else
focused = (wParam.ToInt64() & 0xFFFF) != 0;
if (new_focused_state != Focused)
FocusedChanged(this, EventArgs.Empty);
HandleActivate(handle, message, wParam, lParam);
break;
case WindowMessage.ENTERMENULOOP:
case WindowMessage.ENTERSIZEMOVE:
// Entering the modal size/move loop: we don't want rendering to
// stop during this time, so we register a timer callback to continue
// processing from time to time.
is_in_modal_loop = true;
StartTimer(handle);
if (!CursorVisible)
UngrabCursor();
HandleEnterModalLoop(handle, message, wParam, lParam);
break;
case WindowMessage.EXITMENULOOP:
case WindowMessage.EXITSIZEMOVE:
// Exiting from Modal size/move loop: the timer callback is no longer
// necessary.
is_in_modal_loop = false;
StopTimer(handle);
// Ensure cursor remains grabbed
if (!CursorVisible)
GrabCursor();
HandleExitModalLoop(handle, message, wParam, lParam);
break;
case WindowMessage.ERASEBKGND:
return new IntPtr(1);
case WindowMessage.WINDOWPOSCHANGED:
unsafe
{
WindowPosition* pos = (WindowPosition*)lParam;
if (window != null && pos->hwnd == window.Handle)
{
Point new_location = new Point(pos->x, pos->y);
if (Location != new_location)
{
bounds.Location = new_location;
Move(this, EventArgs.Empty);
}
Size new_size = new Size(pos->cx, pos->cy);
if (Size != new_size)
{
bounds.Width = pos->cx;
bounds.Height = pos->cy;
Win32Rectangle rect;
Functions.GetClientRect(handle, out rect);
client_rectangle = rect.ToRectangle();
Functions.SetWindowPos(child_window.Handle, IntPtr.Zero, 0, 0, ClientRectangle.Width, ClientRectangle.Height,
SetWindowPosFlags.NOZORDER | SetWindowPosFlags.NOOWNERZORDER |
SetWindowPosFlags.NOACTIVATE | SetWindowPosFlags.NOSENDCHANGING);
if (suppress_resize <= 0)
Resize(this, EventArgs.Empty);
}
if (!is_in_modal_loop)
{
// If we are in a modal resize/move loop, cursor grabbing is
// handled inside [ENTER|EXIT]SIZEMOVE case above.
// If not, then we have to handle cursor grabbing here.
if (!CursorVisible)
GrabCursor();
}
}
}
HandleWindowPositionChanged(handle, message, wParam, lParam);
break;
case WindowMessage.STYLECHANGED:
unsafe
{
if (wParam.ToInt64() == (long)GWL.STYLE)
{
WindowStyle style = ((StyleStruct*)lParam)->New;
if ((style & WindowStyle.Popup) != 0)
windowBorder = WindowBorder.Hidden;
else if ((style & WindowStyle.ThickFrame) != 0)
windowBorder = WindowBorder.Resizable;
else if ((style & ~(WindowStyle.ThickFrame | WindowStyle.MaximizeBox)) != 0)
windowBorder = WindowBorder.Fixed;
}
}
// Ensure cursor remains grabbed
if (!CursorVisible)
GrabCursor();
HandleStyleChanged(handle, message, wParam, lParam);
break;
case WindowMessage.SIZE:
SizeMessage state = (SizeMessage)wParam.ToInt64();
WindowState new_state = windowState;
switch (state)
{
case SizeMessage.RESTORED: new_state = borderless_maximized_window_state ?
WindowState.Maximized : WindowState.Normal; break;
case SizeMessage.MINIMIZED: new_state = WindowState.Minimized; break;
case SizeMessage.MAXIMIZED: new_state = WindowBorder == WindowBorder.Hidden ?
WindowState.Fullscreen : WindowState.Maximized;
break;
}
if (new_state != windowState)
{
windowState = new_state;
WindowStateChanged(this, EventArgs.Empty);
// Ensure cursor remains grabbed
if (!CursorVisible)
GrabCursor();
}
HandleSize(handle, message, wParam, lParam);
break;
#endregion
@ -382,84 +609,51 @@ namespace OpenTK.Platform.Windows
#region Input events
case WindowMessage.CHAR:
if (IntPtr.Size == 4)
key_press.KeyChar = (char)wParam.ToInt32();
else
key_press.KeyChar = (char)wParam.ToInt64();
KeyPress(this, key_press);
HandleChar(handle, message, wParam, lParam);
break;
case WindowMessage.MOUSEMOVE:
Point point = new Point(
(short)((uint)lParam.ToInt32() & 0x0000FFFF),
(short)(((uint)lParam.ToInt32() & 0xFFFF0000) >> 16));
mouse.Position = point;
if (mouse_outside_window)
{
// Once we receive a mouse move event, it means that the mouse has
// re-entered the window.
mouse_outside_window = false;
EnableMouseTracking();
MouseEnter(this, EventArgs.Empty);
}
HandleMouseMove(handle, message, wParam, lParam);
break;
case WindowMessage.MOUSELEAVE:
mouse_outside_window = true;
// Mouse tracking is disabled automatically by the OS
MouseLeave(this, EventArgs.Empty);
HandleMouseLeave(handle, message, wParam, lParam);
break;
case WindowMessage.MOUSEWHEEL:
// This is due to inconsistent behavior of the WParam value on 64bit arch, whese
// wparam = 0xffffffffff880000 or wparam = 0x00000000ff100000
mouse.WheelPrecise += ((long)wParam << 32 >> 48) / 120.0f;
HandleMouseWheel(handle, message, wParam, lParam);
break;
case WindowMessage.LBUTTONDOWN:
Functions.SetCapture(window.Handle);
mouse[MouseButton.Left] = true;
HandleLButtonDown(handle, message, wParam, lParam);
break;
case WindowMessage.MBUTTONDOWN:
Functions.SetCapture(window.Handle);
mouse[MouseButton.Middle] = true;
HandleMButtonDown(handle, message, wParam, lParam);
break;
case WindowMessage.RBUTTONDOWN:
Functions.SetCapture(window.Handle);
mouse[MouseButton.Right] = true;
HandleRButtonDown(handle, message, wParam, lParam);
break;
case WindowMessage.XBUTTONDOWN:
Functions.SetCapture(window.Handle);
mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) !=
(int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = true;
HandleXButtonDown(handle, message, wParam, lParam);
break;
case WindowMessage.LBUTTONUP:
Functions.ReleaseCapture();
mouse[MouseButton.Left] = false;
HandleLButtonUp(handle, message, wParam, lParam);
break;
case WindowMessage.MBUTTONUP:
Functions.ReleaseCapture();
mouse[MouseButton.Middle] = false;
HandleMButtonUp(handle, message, wParam, lParam);
break;
case WindowMessage.RBUTTONUP:
Functions.ReleaseCapture();
mouse[MouseButton.Right] = false;
HandleRButtonUp(handle, message, wParam, lParam);
break;
case WindowMessage.XBUTTONUP:
Functions.ReleaseCapture();
mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) !=
(int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = false;
HandleXButtonUp(handle, message, wParam, lParam);
break;
// Keyboard events:
@ -467,47 +661,14 @@ namespace OpenTK.Platform.Windows
case WindowMessage.KEYUP:
case WindowMessage.SYSKEYDOWN:
case WindowMessage.SYSKEYUP:
bool pressed =
message == WindowMessage.KEYDOWN ||
message == WindowMessage.SYSKEYDOWN;
// Shift/Control/Alt behave strangely when e.g. ShiftRight is held down and ShiftLeft is pressed
// and released. It looks like neither key is released in this case, or that the wrong key is
// released in the case of Control and Alt.
// To combat this, we are going to release both keys when either is released. Hacky, but should work.
// Win95 does not distinguish left/right key constants (GetAsyncKeyState returns 0).
// In this case, both keys will be reported as pressed.
bool extended = (lParam.ToInt64() & ExtendedBit) != 0;
short scancode = (short)((lParam.ToInt64() >> 16) & 0xFF);
VirtualKeys vkey = (VirtualKeys)wParam;
bool is_valid;
Key key = KeyMap.TranslateKey(scancode, vkey, extended, false, out is_valid);
if (is_valid)
{
keyboard.SetKey(key, (byte)scancode, pressed);
if (pressed)
{
key_down.Key = key;
KeyDown(this, key_down);
}
else
{
key_up.Key = key;
KeyUp(this, key_up);
}
}
HandleKeyboard(handle, message, wParam, lParam);
return IntPtr.Zero;
case WindowMessage.SYSCHAR:
return IntPtr.Zero;
case WindowMessage.KILLFOCUS:
keyboard.ClearKeys();
HandleKillFocus(handle, message, wParam, lParam);
break;
#endregion
@ -515,44 +676,15 @@ namespace OpenTK.Platform.Windows
#region Creation / Destruction events
case WindowMessage.CREATE:
CreateStruct cs = (CreateStruct)Marshal.PtrToStructure(lParam, typeof(CreateStruct));
if (cs.hwndParent == IntPtr.Zero)
{
bounds.X = cs.x;
bounds.Y = cs.y;
bounds.Width = cs.cx;
bounds.Height = cs.cy;
Win32Rectangle rect;
Functions.GetClientRect(handle, out rect);
client_rectangle = rect.ToRectangle();
invisible_since_creation = true;
}
HandleCreate(handle, message, wParam, lParam);
break;
case WindowMessage.CLOSE:
System.ComponentModel.CancelEventArgs e = new System.ComponentModel.CancelEventArgs();
Closing(this, e);
if (!e.Cancel)
{
DestroyWindow();
break;
}
HandleClose(handle, message, wParam, lParam);
return IntPtr.Zero;
case WindowMessage.DESTROY:
exists = false;
Functions.UnregisterClass(ClassName, Instance);
window.Dispose();
child_window.Dispose();
Closed(this, EventArgs.Empty);
HandleDestroy(handle, message, wParam, lParam);
break;
#endregion
@ -1117,34 +1249,35 @@ namespace OpenTK.Platform.Windows
WindowState state = WindowState;
ResetWindowState();
WindowStyle style = WindowStyle.ClipChildren | WindowStyle.ClipSiblings;
WindowStyle old_style = WindowStyle.ClipChildren | WindowStyle.ClipSiblings;
WindowStyle new_style = old_style;
switch (value)
{
case WindowBorder.Resizable:
style |= WindowStyle.OverlappedWindow;
new_style |= WindowStyle.OverlappedWindow;
break;
case WindowBorder.Fixed:
style |= WindowStyle.OverlappedWindow &
new_style |= WindowStyle.OverlappedWindow &
~(WindowStyle.ThickFrame | WindowStyle.MaximizeBox | WindowStyle.SizeBox);
break;
case WindowBorder.Hidden:
style |= WindowStyle.Popup;
new_style |= WindowStyle.Popup;
break;
}
// Make sure client size doesn't change when changing the border style.
Size client_size = ClientSize;
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.
if (was_visible)
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,
SetWindowPosFlags.NOMOVE | SetWindowPosFlags.NOZORDER |
SetWindowPosFlags.FRAMECHANGED);
@ -1157,7 +1290,23 @@ namespace OpenTK.Platform.Windows
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
{
#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();
#endregion
readonly IntPtr Device;
readonly List<GraphicsMode> modes = new List<GraphicsMode>();
#region Constructors
public WinGraphicsMode(IntPtr device)
{
lock (SyncRoot)
{
modes.AddRange(GetModesARB(device));
if (modes.Count == 0)
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());
}
if (device == IntPtr.Zero)
throw new ArgumentException();
Device = device;
}
#endregion
@ -67,167 +68,341 @@ namespace OpenTK.Platform.Windows
public GraphicsMode SelectGraphicsMode(ColorFormat color, int depth, int stencil, int samples,
ColorFormat accum, int buffers, bool stereo)
{
GraphicsMode mode = null;
do
GraphicsMode mode = new GraphicsMode(color, depth, stencil, samples,accum, buffers, stereo);
GraphicsMode created_mode = ChoosePixelFormatARB(Device, mode);
// If ChoosePixelFormatARB failed, iterate through all acceleration types in turn (ICD, MCD, None)
// 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);
if (created_mode == null)
{
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));
throw new GraphicsModeException("The requested GraphicsMode is not supported");
}
if (mode == null)
mode = modes[0];
return mode;
}
bool RelaxParameters(ref ColorFormat color, ref int depth, ref int stencil, ref int samples,
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.
return created_mode;
}
#endregion
#region Private Methods
#region GetModesPFD
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
#region ChoosePixelFormatARB
// Queries pixel formats through the WGL_ARB_pixel_format extension
// 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
// connection), this method will return 0 formats and we will fall back to
// GetModesPFD.
IEnumerable<GraphicsMode> GetModesARB(IntPtr device)
// ChoosePixelFormatPFD.
GraphicsMode ChoosePixelFormatARB(IntPtr device, GraphicsMode mode)
{
// See http://www.opengl.org/registry/specs/ARB/wgl_pixel_format.txt
// for more details
Debug.Write("Retrieving ARB pixel formats.... ");
if (Wgl.Delegates.wglChoosePixelFormatARB == null || Wgl.Delegates.wglGetPixelFormatAttribivARB == null)
GraphicsMode created_mode = null;
if (Wgl.SupportsExtension("WGL_ARB_pixel_format") &&
Wgl.Delegates.wglChoosePixelFormatARB != null)
{
Debug.WriteLine("failed.");
yield break;
List<int> attributes = new List<int>();
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");
}
// 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.
int[] attribs = new int[]
return created_mode;
}
#endregion
#region ChoosePixelFormatPFD
static bool Compare(int got, int requested, ref int distance)
{
bool valid = true;
if (got == 0 && requested != 0)
{
(int)WGL_ARB_pixel_format.AccelerationArb,
(int)WGL_ARB_pixel_format.RedBitsArb,
(int)WGL_ARB_pixel_format.GreenBitsArb,
(int)WGL_ARB_pixel_format.BlueBitsArb,
(int)WGL_ARB_pixel_format.AlphaBitsArb,
(int)WGL_ARB_pixel_format.ColorBitsArb,
(int)WGL_ARB_pixel_format.DepthBitsArb,
(int)WGL_ARB_pixel_format.StencilBitsArb,
(int)WGL_ARB_multisample.SampleBuffersArb,
(int)WGL_ARB_multisample.SamplesArb,
(int)WGL_ARB_pixel_format.AccumRedBitsArb,
(int)WGL_ARB_pixel_format.AccumGreenBitsArb,
(int)WGL_ARB_pixel_format.AccumBlueBitsArb,
(int)WGL_ARB_pixel_format.AccumAlphaBitsArb,
(int)WGL_ARB_pixel_format.AccumBitsArb,
(int)WGL_ARB_pixel_format.DoubleBufferArb,
(int)WGL_ARB_pixel_format.StereoArb,
0
};
// Allocate storage for the results of GetPixelFormatAttrib queries
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))
// mode does not support the requested feature.
valid = false;
}
else if (got >= requested)
{
for (int p = 1; p < num_formats; p++)
// 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)
{
// Get the format attributes for this pixel format
if (!Wgl.Arb.GetPixelFormatAttrib(device, p, 0, attribs.Length - 1, attribs, values))
{
Debug.Print("[Warning] Failed to detect attributes for PixelFormat:{0}.", p);
continue;
}
type = AccelerationType.MCD;
}
else
{
type = AccelerationType.None;
}
}
return type;
}
// Skip formats that don't offer full hardware acceleration
WGL_ARB_pixel_format acceleration = (WGL_ARB_pixel_format)attribs[0];
if (acceleration != WGL_ARB_pixel_format.FullAccelerationArb)
{
continue;
}
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.
// The results will be stored in the 'values' array below.
int[] attribs = new int[]
{
(int)WGL_ARB_pixel_format.AccelerationArb,
(int)WGL_ARB_pixel_format.RedBitsArb,
(int)WGL_ARB_pixel_format.GreenBitsArb,
(int)WGL_ARB_pixel_format.BlueBitsArb,
(int)WGL_ARB_pixel_format.AlphaBitsArb,
(int)WGL_ARB_pixel_format.ColorBitsArb,
(int)WGL_ARB_pixel_format.DepthBitsArb,
(int)WGL_ARB_pixel_format.StencilBitsArb,
(int)WGL_ARB_multisample.SampleBuffersArb,
(int)WGL_ARB_multisample.SamplesArb,
(int)WGL_ARB_pixel_format.AccumRedBitsArb,
(int)WGL_ARB_pixel_format.AccumGreenBitsArb,
(int)WGL_ARB_pixel_format.AccumBlueBitsArb,
(int)WGL_ARB_pixel_format.AccumAlphaBitsArb,
(int)WGL_ARB_pixel_format.AccumBitsArb,
(int)WGL_ARB_pixel_format.DoubleBufferArb,
(int)WGL_ARB_pixel_format.StereoArb,
0
};
// Allocate storage for the results of GetPixelFormatAttrib queries
int[] values = new int[attribs.Length];
// Get the format attributes for this pixel format
if (!Wgl.Arb.GetPixelFormatAttrib(device, pixelformat, 0, attribs.Length - 1, attribs, values))
{
Debug.Print("[Warning] Failed to detect attributes for PixelFormat: {0}.", pixelformat);
}
// Skip formats that don't offer full hardware acceleration
WGL_ARB_pixel_format acceleration = (WGL_ARB_pixel_format)values[0];
if (acceleration == WGL_ARB_pixel_format.FullAccelerationArb)
{
// 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]),
values[6],
values[7],
@ -235,28 +410,9 @@ namespace OpenTK.Platform.Windows
new ColorFormat(values[10], values[11], values[12], values[13]),
values[15] == 1 ? 2 : 1,
values[16] == 1 ? true : false);
yield return 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;
return created_mode;
}
#endregion

View file

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

View file

@ -36,7 +36,7 @@ using System.Diagnostics;
namespace OpenTK.Platform.Windows
{
sealed class WinMMJoystick : IJoystickDriver, IGamePadDriver
sealed class WinMMJoystick : IJoystickDriver, IJoystickDriver2
{
#region Fields
@ -133,6 +133,27 @@ namespace OpenTK.Platform.Windows
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
#region IJoystickDriver
@ -237,6 +258,95 @@ namespace OpenTK.Platform.Windows
#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
public void Dispose()
@ -378,9 +488,11 @@ namespace OpenTK.Platform.Windows
[DllImport("Winmm.dll"), SuppressUnmanagedCodeSecurity]
public static extern JoystickError joyGetDevCaps(int uJoyID, out JoyCaps pjc, int cbjc);
[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]
public static extern int joyGetNumDevs();
[DllImport("Winmm.dll"), SuppressUnmanagedCodeSecurity]
public static extern JoystickError joyConfigChanged(int flags);
}
#endregion
@ -427,21 +539,5 @@ namespace OpenTK.Platform.Windows
}
#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;
WinRawMouse mouse_driver;
WinMMJoystick joystick_driver;
IGamePadDriver gamepad_driver;
IJoystickDriver2 joystick_driver;
IntPtr DevNotifyHandle;
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);
mouse_driver = new WinRawMouse(Parent.Handle);
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);
}
@ -186,6 +196,11 @@ namespace OpenTK.Platform.Windows
}
public override IGamePadDriver GamePadDriver
{
get { return gamepad_driver; }
}
public override IJoystickDriver2 JoystickDriver
{
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
// keyboard device by qeurying the registry.
RegistryKey regkey = GetRegistryKey(name);
string deviceDesc = (string)regkey.GetValue("DeviceDesc");
if (regkey == null)
continue;
string deviceDesc = (string)regkey.GetValue("DeviceDesc");
string deviceClass = (string)regkey.GetValue("Class");
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...
@ -205,10 +206,15 @@ namespace OpenTK.Platform.Windows
static RegistryKey GetRegistryKey(string name)
{
if (name.Length < 4)
return null;
// remove the \??\
name = name.Substring(4);
string[] split = name.Split('#');
if (split.Length < 3)
return null;
string id_01 = split[0]; // ACPI (Class 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
// mouse device by qeurying the registry.
RegistryKey regkey = FindRegistryKey(name);
string deviceDesc = (string)regkey.GetValue("DeviceDesc");
if (regkey == null)
continue;
string deviceDesc = (string)regkey.GetValue("DeviceDesc");
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
string deviceClassGUID = (string)regkey.GetValue("ClassGUID");
RegistryKey classGUIDKey = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Class\" + deviceClassGUID);
@ -266,10 +268,15 @@ namespace OpenTK.Platform.Windows
static RegistryKey FindRegistryKey(string name)
{
if (name.Length < 4)
return null;
// remove the \??\
name = name.Substring(4);
string[] split = name.Split('#');
if (split.Length < 3)
return null;
string id_01 = split[0]; // ACPI (Class 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);
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.RefreshRate = current_refresh_rate;
dev.AvailableResolutions = available_res;

View file

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

View file

@ -59,6 +59,7 @@ namespace OpenTK.Platform.X11
// Legacy input support
X11Input driver;
KeyboardDevice keyboard;
MouseDevice mouse;
// Window manager hints for fullscreen windows.
@ -119,6 +120,8 @@ namespace OpenTK.Platform.X11
readonly byte[] ascii = new byte[16];
readonly char[] chars = new char[16];
readonly KeyPressEventArgs KPEventArgs = new KeyPressEventArgs('\0');
readonly KeyboardKeyEventArgs KeyDownEventArgs = new KeyboardKeyEventArgs();
readonly KeyboardKeyEventArgs KeyUpEventArgs = new KeyboardKeyEventArgs();
readonly IntPtr EmptyCursor;
@ -209,6 +212,7 @@ namespace OpenTK.Platform.X11
RefreshWindowBounds(ref e);
driver = new X11Input(window);
keyboard = driver.Keyboard[0];
mouse = driver.Mouse[0];
EmptyCursor = CreateEmptyCursor(window);
@ -256,7 +260,7 @@ namespace OpenTK.Platform.X11
#endregion
#region Private Members
#region private void RegisterAtoms()
/// <summary>
@ -310,6 +314,11 @@ namespace OpenTK.Platform.X11
#endregion
#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)
{
@ -617,46 +626,63 @@ namespace OpenTK.Platform.X11
bool RefreshWindowBorders()
{
IntPtr atom, nitems, bytes_after, prop = IntPtr.Zero;
int format;
bool borders_changed = false;
using (new XLock(window.Display))
if (IsWindowBorderHidden)
{
Functions.XGetWindowProperty(window.Display, window.Handle,
_atom_net_frame_extents, IntPtr.Zero, new IntPtr(16), false,
(IntPtr)Atom.XA_CARDINAL, out atom, out format, out nitems, out bytes_after, ref prop);
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;
}
if ((prop != IntPtr.Zero))
else
{
if ((long)nitems == 4)
{
int new_border_left = Marshal.ReadIntPtr(prop, 0).ToInt32();
int new_border_right = Marshal.ReadIntPtr(prop, IntPtr.Size).ToInt32();
int new_border_top = Marshal.ReadIntPtr(prop, IntPtr.Size * 2).ToInt32();
int new_border_bottom = Marshal.ReadIntPtr(prop, IntPtr.Size * 3).ToInt32();
borders_changed =
new_border_left != border_left ||
new_border_right != border_right ||
new_border_top != border_top ||
new_border_bottom != border_bottom;
border_left = new_border_left;
border_right = new_border_right;
border_top = new_border_top;
border_bottom = new_border_bottom;
//Debug.WriteLine(border_left);
//Debug.WriteLine(border_right);
//Debug.WriteLine(border_top);
//Debug.WriteLine(border_bottom);
}
IntPtr atom, nitems, bytes_after, prop = IntPtr.Zero;
int format;
using (new XLock(window.Display))
{
Functions.XFree(prop);
Functions.XGetWindowProperty(window.Display, window.Handle,
_atom_net_frame_extents, IntPtr.Zero, new IntPtr(16), false,
(IntPtr)Atom.XA_CARDINAL, out atom, out format, out nitems, out bytes_after, ref prop);
}
if ((prop != IntPtr.Zero))
{
if ((long)nitems == 4)
{
int new_border_left = Marshal.ReadIntPtr(prop, 0).ToInt32();
int new_border_right = Marshal.ReadIntPtr(prop, IntPtr.Size).ToInt32();
int new_border_top = Marshal.ReadIntPtr(prop, IntPtr.Size * 2).ToInt32();
int new_border_bottom = Marshal.ReadIntPtr(prop, IntPtr.Size * 3).ToInt32();
borders_changed =
new_border_left != border_left ||
new_border_right != border_right ||
new_border_top != border_top ||
new_border_bottom != border_bottom;
border_left = new_border_left;
border_right = new_border_right;
border_top = new_border_top;
border_bottom = new_border_bottom;
//Debug.WriteLine(border_left);
//Debug.WriteLine(border_right);
//Debug.WriteLine(border_top);
//Debug.WriteLine(border_bottom);
}
using (new XLock(window.Display))
{
Functions.XFree(prop);
}
}
}
@ -667,9 +693,30 @@ namespace OpenTK.Platform.X11
{
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(
e.ConfigureEvent.x - border_left,
e.ConfigureEvent.y - border_top);
x - border_left,
y - border_top);
if (Location != new_location)
{
bounds.Location = new_location;
@ -688,6 +735,8 @@ namespace OpenTK.Platform.X11
Resize(this, EventArgs.Empty);
}
//Debug.Print("[X11] Window bounds changed: {0}", bounds);
}
static IntPtr CreateEmptyCursor(X11WindowInfo window)
@ -795,29 +844,50 @@ namespace OpenTK.Platform.X11
break;
case XEventName.KeyPress:
driver.ProcessEvent(ref e);
int status = 0;
status = Functions.XLookupString(ref e.KeyEvent, ascii, ascii.Length, null, IntPtr.Zero);
Encoding.Default.GetChars(ascii, 0, status, chars, 0);
EventHandler<KeyPressEventArgs> key_press = KeyPress;
if (key_press != null)
case XEventName.KeyRelease:
bool pressed = e.type == XEventName.KeyPress;
Key key;
if (driver.TranslateKey(ref e.KeyEvent, out key))
{
for (int i = 0; i < status; i++)
if (pressed)
{
KPEventArgs.KeyChar = chars[i];
key_press(this, KPEventArgs);
// 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;
status = Functions.XLookupString(
ref e.KeyEvent, ascii, ascii.Length, null, IntPtr.Zero);
Encoding.Default.GetChars(ascii, 0, status, chars, 0);
for (int i = 0; i < status; i++)
{
if (!Char.IsControl(chars[i]))
{
KPEventArgs.KeyChar = chars[i];
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;
case XEventName.MotionNotify:
{
// Try to detect and ignore events from XWarpPointer, below.
@ -926,17 +996,44 @@ namespace OpenTK.Platform.X11
public Rectangle Bounds
{
get { return bounds; }
get
{
return bounds;
}
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))
{
Functions.XMoveResizeWindow(window.Display, window.Handle,
value.X,
value.Y,
value.Width - border_left - border_right,
value.Height - border_top - border_bottom);
if (is_location_changed && is_size_changed)
{
Functions.XMoveResizeWindow(window.Display, window.Handle,
x, y, width, height);
}
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();
}
}
@ -950,11 +1047,7 @@ namespace OpenTK.Platform.X11
get { return Bounds.Location; }
set
{
using (new XLock(window.Display))
{
Functions.XMoveWindow(window.Display, window.Handle, value.X, value.Y);
}
ProcessEvents();
Bounds = new Rectangle(value, Bounds.Size);
}
}
@ -967,16 +1060,7 @@ namespace OpenTK.Platform.X11
get { return Bounds.Size; }
set
{
int width = value.Width - border_left - border_right;
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();
Bounds = new Rectangle(Bounds.Location, value);
}
}

View file

@ -39,19 +39,68 @@ namespace OpenTK.Platform.X11
// The actual GraphicsMode that will be selected.
IntPtr visual = IntPtr.Zero;
IntPtr display = API.DefaultDisplay;
// 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.
visual = SelectVisualUsingFBConfig(color, depth, stencil, samples, accum, buffers, stereo);
if (visual == IntPtr.Zero)
visual = SelectVisualUsingChooseVisual(color, depth, stencil, samples, accum, buffers, stereo);
if (visual == IntPtr.Zero)
throw new GraphicsModeException("Requested GraphicsMode not available.");
do
{
// 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.
visual = SelectVisualUsingFBConfig(color, depth, stencil, samples, accum, buffers, stereo);
if (visual == IntPtr.Zero)
visual = SelectVisualUsingChooseVisual(color, depth, stencil, samples, accum, buffers, stereo);
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.");
}
}
while (visual == IntPtr.Zero);
XVisualInfo info = (XVisualInfo)Marshal.PtrToStructure(visual, typeof(XVisualInfo));
// See what we *really* got:
int r, g, b, a;
Glx.GetConfig(display, ref info, GLXAttribute.ALPHA_SIZE, out a);

View file

@ -78,7 +78,6 @@ namespace OpenTK.Platform.X11
Marshal.PtrToStructure(keysym_ptr, keysyms);
API.Free(keysym_ptr);
KeyboardDevice kb = new KeyboardDevice();
keyboard.Description = "Default X11 keyboard";
keyboard.NumberOfKeys = lastKeyCode - firstKeyCode + 1;
keyboard.DeviceID = IntPtr.Zero;
@ -148,29 +147,38 @@ namespace OpenTK.Platform.X11
#endif
#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)
internal void ProcessEvent(ref XEvent e)
{
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:
if (e.ButtonEvent.button == 1) mouse[OpenTK.Input.MouseButton.Left] = 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 { }
sealed class X11Joystick : IJoystickDriver, IGamePadDriver
sealed class X11Joystick : IJoystickDriver, IJoystickDriver2, IGamePadDriver
{
#region Fields
@ -259,21 +259,47 @@ namespace OpenTK.Platform.X11
#endregion
//HACK implement
public GamePadState GetState()
#region IGamePadDriver Members
public GamePadCapabilities GetCapabilities(int index)
{
throw new NotImplementedException();
return new GamePadCapabilities();
}
public GamePadState GetState(int index)
{
Poll();
throw new NotImplementedException();
return new GamePadState();
}
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
/// DPI-aware (e.g. WinForms.)
/// </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; }
/// <summary>