mirror of
https://github.com/Ryujinx/Opentk.git
synced 2025-01-10 18:55:30 +00:00
Merge branch 'develop' into utf8
This commit is contained in:
commit
9268b5ed7f
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -70,6 +70,7 @@ Binaries/
|
|||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
|
|
|
@ -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">
|
||||
|
|
81
Source/Examples/OpenTK/Test/ExternalContext.cs
Normal file
81
Source/Examples/OpenTK/Test/ExternalContext.cs
Normal 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
|
||||
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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()]
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
166
Source/OpenTK/Input/Buttons.cs
Normal file
166
Source/OpenTK/Input/Buttons.cs
Normal 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,
|
||||
}
|
||||
}
|
41
Source/OpenTK/Input/ConfigurationType.cs
Normal file
41
Source/OpenTK/Input/ConfigurationType.cs
Normal 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
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
216
Source/OpenTK/Input/GamePadButtons.cs
Normal file
216
Source/OpenTK/Input/GamePadButtons.cs
Normal 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
|
||||
}
|
||||
}
|
||||
|
395
Source/OpenTK/Input/GamePadCapabilities.cs
Normal file
395
Source/OpenTK/Input/GamePadCapabilities.cs
Normal 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
|
||||
}
|
||||
}
|
||||
|
204
Source/OpenTK/Input/GamePadConfiguration.cs
Normal file
204
Source/OpenTK/Input/GamePadConfiguration.cs
Normal 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
|
||||
}
|
||||
}
|
132
Source/OpenTK/Input/GamePadConfigurationDatabase.cs
Normal file
132
Source/OpenTK/Input/GamePadConfigurationDatabase.cs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
58
Source/OpenTK/Input/GamePadConfigurationItem.cs
Normal file
58
Source/OpenTK/Input/GamePadConfigurationItem.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
72
Source/OpenTK/Input/GamePadConfigurationSource.cs
Normal file
72
Source/OpenTK/Input/GamePadConfigurationSource.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
}
|
72
Source/OpenTK/Input/GamePadConfigurationTarget.cs
Normal file
72
Source/OpenTK/Input/GamePadConfigurationTarget.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
}
|
223
Source/OpenTK/Input/GamePadDPad.cs
Normal file
223
Source/OpenTK/Input/GamePadDPad.cs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
142
Source/OpenTK/Input/GamePadThumbSticks.cs
Normal file
142
Source/OpenTK/Input/GamePadThumbSticks.cs
Normal 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
|
||||
}
|
||||
}
|
136
Source/OpenTK/Input/GamePadTriggers.cs
Normal file
136
Source/OpenTK/Input/GamePadTriggers.cs
Normal 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
|
||||
}
|
||||
}
|
97
Source/OpenTK/Input/GamePadType.cs
Normal file
97
Source/OpenTK/Input/GamePadType.cs
Normal 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,
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,5 +37,6 @@ namespace OpenTK.Input
|
|||
IMouseDriver2 MouseDriver { get; }
|
||||
IKeyboardDriver2 KeyboardDriver { get; }
|
||||
IGamePadDriver GamePadDriver { get; }
|
||||
IJoystickDriver2 JoystickDriver { get; }
|
||||
}
|
||||
}
|
||||
|
|
42
Source/OpenTK/Input/IJoystickDriver2.cs
Normal file
42
Source/OpenTK/Input/IJoystickDriver2.cs
Normal 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);
|
||||
}
|
||||
}
|
92
Source/OpenTK/Input/Joystick.cs
Normal file
92
Source/OpenTK/Input/Joystick.cs
Normal 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);
|
||||
//}
|
||||
}
|
||||
}
|
66
Source/OpenTK/Input/JoystickAxis.cs
Normal file
66
Source/OpenTK/Input/JoystickAxis.cs
Normal 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,
|
||||
}
|
||||
}
|
76
Source/OpenTK/Input/JoystickButton.cs
Normal file
76
Source/OpenTK/Input/JoystickButton.cs
Normal 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,
|
||||
}
|
||||
}
|
156
Source/OpenTK/Input/JoystickCapabilities.cs
Normal file
156
Source/OpenTK/Input/JoystickCapabilities.cs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
260
Source/OpenTK/Input/JoystickState.cs
Normal file
260
Source/OpenTK/Input/JoystickState.cs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -291,6 +291,11 @@ namespace OpenTK.Input
|
|||
IsConnected |= other.IsConnected;
|
||||
}
|
||||
|
||||
internal void SetIsConnected(bool value)
|
||||
{
|
||||
IsConnected = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Members
|
||||
|
|
|
@ -329,6 +329,11 @@ namespace OpenTK.Input
|
|||
}
|
||||
}
|
||||
|
||||
internal void SetIsConnected(bool value)
|
||||
{
|
||||
IsConnected = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Members
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -52,5 +52,7 @@ namespace OpenTK.Platform
|
|||
OpenTK.Input.IMouseDriver2 CreateMouseDriver();
|
||||
|
||||
OpenTK.Input.IGamePadDriver CreateGamePadDriver();
|
||||
|
||||
Input.IJoystickDriver2 CreateJoystickDriver();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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); }
|
||||
}
|
||||
|
||||
|
|
|
@ -113,5 +113,10 @@ namespace OpenTK.Platform.MacOS
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public IJoystickDriver2 JoystickDriver
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
92
Source/OpenTK/Platform/MacOS/NS.cs
Normal file
92
Source/OpenTK/Platform/MacOS/NS.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
196
Source/OpenTK/Platform/MappedGamePadDriver.cs
Normal file
196
Source/OpenTK/Platform/MappedGamePadDriver.cs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -130,7 +130,12 @@ namespace OpenTK.Platform.Windows
|
|||
{
|
||||
return InputDriver.GamePadDriver;
|
||||
}
|
||||
|
||||
|
||||
public IJoystickDriver2 CreateJoystickDriver()
|
||||
{
|
||||
return InputDriver.JoystickDriver;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
IInputDriver2 InputDriver
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
405
Source/OpenTK/Platform/Windows/XInputJoystick.cs
Normal file
405
Source/OpenTK/Platform/Windows/XInputJoystick.cs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -98,6 +98,12 @@ namespace OpenTK.Platform.X11
|
|||
return new X11Joystick();
|
||||
}
|
||||
|
||||
public virtual OpenTK.Input.IJoystickDriver2 CreateJoystickDriver()
|
||||
{
|
||||
return new X11Joystick();
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable Members
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue