diff --git a/QuickStart.sln b/QuickStart.sln new file mode 100644 index 00000000..510dc6f4 --- /dev/null +++ b/QuickStart.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickStart", "Source\QuickStart\QuickStart.csproj", "{90762BBE-CB23-42FF-9D3E-486D2F6CA485}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|AnyCPU = Debug|AnyCPU + Release|AnyCPU = Release|AnyCPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {90762BBE-CB23-42FF-9D3E-486D2F6CA485}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU + {90762BBE-CB23-42FF-9D3E-486D2F6CA485}.Debug|AnyCPU.Build.0 = Debug|Any CPU + {90762BBE-CB23-42FF-9D3E-486D2F6CA485}.Release|AnyCPU.ActiveCfg = Release|Any CPU + {90762BBE-CB23-42FF-9D3E-486D2F6CA485}.Release|AnyCPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = Source\QuickStart\QuickStart.csproj + EndGlobalSection +EndGlobal diff --git a/Source/Bind/Generator.Bind.csproj b/Source/Bind/Generator.Bind.csproj index 09384123..da13b692 100644 --- a/Source/Bind/Generator.Bind.csproj +++ b/Source/Bind/Generator.Bind.csproj @@ -1,4 +1,4 @@ - + Local @@ -7,8 +7,6 @@ {31D19132-0000-0000-0000-000000000000} Debug AnyCPU - - Bind @@ -21,8 +19,6 @@ Bind - - @@ -47,73 +43,60 @@ true - False 285212672 - False DEBUG;TRACE; - True + true 4096 - False + false ..\..\Binaries\OpenTK\Debug\ False False - False 4 - False - - AllRules.ruleset + full - False 285212672 - False TRACE; - False 4096 - True + true ..\..\Binaries\OpenTK\Release\ False False - False 4 - False - - AllRules.ruleset + none - False 285212672 - False TRACE; - False 4096 - True + true ..\..\Binaries\OpenTK\Release\ False False - False 4 - False - - AllRules.ruleset + none ..\..\Binaries\OpenTK\Release\ + none + 4 + true + TRACE; true @@ -1055,6 +1038,7 @@ + diff --git a/Source/Build.UpdateVersion/Build.UpdateVersion.csproj b/Source/Build.UpdateVersion/Build.UpdateVersion.csproj index 071361e1..34d60ecf 100644 --- a/Source/Build.UpdateVersion/Build.UpdateVersion.csproj +++ b/Source/Build.UpdateVersion/Build.UpdateVersion.csproj @@ -1,4 +1,4 @@ - + ..\..\Binaries\OpenTK\Release @@ -26,17 +26,45 @@ Build.UpdateVersion Exe v2.0 + Debug + AnyCPU + 10.0.0 + 2.0 + + + + + + none + TRACE; + 4 + + + true + full + 4 + + + none + TRACE; + 4 + + + none + TRACE; + 4 + diff --git a/Source/Build.UpdateVersion/Program.cs b/Source/Build.UpdateVersion/Program.cs index 0f53c609..9e7a9303 100644 --- a/Source/Build.UpdateVersion/Program.cs +++ b/Source/Build.UpdateVersion/Program.cs @@ -26,20 +26,38 @@ #endregion using System; +using System.Diagnostics; using System.IO; namespace Build.UpdateVersion { - class Program - { + class Program + { const string Major = "1"; const string Minor = "1"; + static string RootDirectory; + static string SourceDirectory; + public static void Main() { + string wdir = Environment.CurrentDirectory; + if (Directory.GetParent(wdir).Name == "Source") + { + // Running through msbuild inside Source/Build.UpdateVersion/ + RootDirectory = "../.."; + SourceDirectory = ".."; + } + else + { + // Running manually inside Binaries/OpenTK/[Debug|Release]/ + RootDirectory = "../../.."; + SourceDirectory = "../../../Source"; + } + DateTime now = DateTime.UtcNow; - GenerateVersionInfo(now, "../../Version.txt"); - GenerateAssemblyInfo(now, "../GlobalAssemblyInfo.cs"); + GenerateVersionInfo(now, Path.Combine(RootDirectory, "Version.txt")); + GenerateAssemblyInfo(now, Path.Combine(SourceDirectory, "GlobalAssemblyInfo.cs")); } static void GenerateVersionInfo(DateTime now, string file) @@ -54,7 +72,7 @@ namespace Build.UpdateVersion version = lines[0]; } } - + // If the file does not exist, create it. if (version == null) { @@ -69,7 +87,9 @@ namespace Build.UpdateVersion // Revision number is defined as the fraction of the current day, expressed in seconds. double timespan = now.Subtract(new DateTime(2010, 1, 1)).TotalDays; string build = ((int)timespan).ToString(); - string revision = ((int)((timespan - (int)timespan) * UInt16.MaxValue)).ToString(); + + string revision = RetrieveSvnRevision() ?? RetrieveBzrRevision() ?? RetrieveSeconds(timespan); + revision = revision.Trim(); File.WriteAllLines(file, new string[] { @@ -90,5 +110,59 @@ namespace Build.UpdateVersion String.Format("[assembly: AssemblyFileVersion(\"{0}.{1}.{2}.{3}\")]", Major, Minor, build, revision), }); } - } -} + + static string RetrieveSeconds(double timespan) + { + string revision = ((int)((timespan - (int)timespan) * UInt16.MaxValue)).ToString(); + return revision; + } + + static string RetrieveSvnRevision() + { + try + { + string output = RunProcess("svn", "info", RootDirectory); + + const string RevisionText = "Revision: "; + int index = output.IndexOf(RevisionText); + if (index > -1) + return output.Substring(index + RevisionText.Length, 5) + .Replace('\r', ' ').Replace('\n', ' ').Trim(); + } + catch (Exception e) + { + Debug.Print("Failed to retrieve svn revision. Error: {0}", e); + } + return null; + } + + static string RetrieveBzrRevision() + { + try + { + string output = RunProcess("bzr", "revno", RootDirectory); + return output != null && !output.StartsWith("bzr") ? output : null; + } + catch (Exception e) + { + Debug.Print("Failed to retrieve svn revision. Error: {0}", e); + } + return null; + } + + static string RunProcess(string cmd, string args, string wdir) + { + ProcessStartInfo info = new ProcessStartInfo(cmd, args); + info.WorkingDirectory = wdir; + info.RedirectStandardOutput = true; + info.RedirectStandardError = true; + info.UseShellExecute = false; + Process p = new Process(); + p.StartInfo = info; + p.Start(); + p.WaitForExit(); + string output = p.StandardOutput.ReadToEnd(); + return output; + } + } +} \ No newline at end of file diff --git a/Source/Compatibility/Audio/OpenAL/AL/EffectsExtensionPresets.cs b/Source/Compatibility/Audio/OpenAL/AL/EffectsExtensionPresets.cs index 71b08db5..3cc16afc 100644 --- a/Source/Compatibility/Audio/OpenAL/AL/EffectsExtensionPresets.cs +++ b/Source/Compatibility/Audio/OpenAL/AL/EffectsExtensionPresets.cs @@ -363,7 +363,5 @@ namespace OpenTK.Audio public static EaxReverb Chapel = new EaxReverb(26, 19.6f, 0.840f, -1000, -500, 0, 4.62f, 0.64f, 1.23f, -700, 0.032f, 0f, 0f, 0f, -200, 0.049f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0.110f, -5f, 5000f, 250f, 0f, 0x3f); public static EaxReverb Smallwaterroom = new EaxReverb(26, 36.2f, 0.700f, -1000, -698, 0, 1.51f, 1.25f, 1.14f, -100, 0.020f, 0f, 0f, 0f, 300, 0.030f, 0f, 0f, 0f, 0.179f, 0.150f, 0.895f, 0.190f, -7f, 5000f, 250f, 0f, 0x0); } - -#pragma warning restore 1591 } } diff --git a/Source/Compatibility/Graphics/GL/GLHelper.cs b/Source/Compatibility/Graphics/GL/GLHelper.cs index 3a293fce..0a1d954a 100644 --- a/Source/Compatibility/Graphics/GL/GLHelper.cs +++ b/Source/Compatibility/Graphics/GL/GLHelper.cs @@ -1312,11 +1312,6 @@ namespace OpenTK.Graphics #endregion -#pragma warning restore 3019 -#pragma warning restore 1591 -#pragma warning restore 1572 -#pragma warning restore 1573 - #endregion } } diff --git a/Source/Compatibility/OpenTK.Compatibility.csproj b/Source/Compatibility/OpenTK.Compatibility.csproj index 6a47f980..cd359462 100644 --- a/Source/Compatibility/OpenTK.Compatibility.csproj +++ b/Source/Compatibility/OpenTK.Compatibility.csproj @@ -1,4 +1,4 @@ - + Local @@ -47,7 +47,8 @@ DEBUG;TRACE; - OpenTK.Compatibility.xml + + true 4096 false @@ -55,7 +56,7 @@ False False 4 - 1591 + 0219, 0414, 0612, 0618, 1591, 3005, 3006 AllRules.ruleset full @@ -65,19 +66,25 @@ TRACE; - OpenTK.Compatibility.xml + + 4096 true ..\..\Binaries\OpenTK\Release\ False False 4 - 1591 + 0219, 0414, 0612, 0618, 1591, 3005, 3006 AllRules.ruleset none ..\..\Binaries\OpenTK\Release\ + none + 4 + 0219, 0414, 0612, 0618, 1591, 3005, 3006 + true + TRACE; true @@ -85,14 +92,15 @@ TRACE; - OpenTK.Compatibility.xml + + 4096 true ..\..\Binaries\OpenTK\Release\ False False 4 - 1591 + 0219, 0414, 0612, 0618, 1591, 3005, 3006 AllRules.ruleset none @@ -475,6 +483,11 @@ Always + + + PreserveNewest + + diff --git a/Source/Converter/Generator.Convert.csproj b/Source/Converter/Generator.Convert.csproj index 76a4bda5..158ce6cf 100644 --- a/Source/Converter/Generator.Convert.csproj +++ b/Source/Converter/Generator.Convert.csproj @@ -1,4 +1,4 @@ - + Local @@ -7,8 +7,6 @@ {5FDFF4B6-0000-0000-0000-000000000000} Debug AnyCPU - - Convert @@ -21,8 +19,6 @@ Convert - - @@ -45,73 +41,60 @@ true - False 285212672 - False DEBUG;TRACE; - True + true 4096 - False + false ..\..\Binaries\OpenTK\Debug\ False False - False 4 - False - - AllRules.ruleset + full - False 285212672 - False TRACE; - False 4096 - True + true ..\..\Binaries\OpenTK\Release\ False False - False 4 - False - - AllRules.ruleset + none ..\..\Binaries\OpenTK\Release\ + none + 4 + true + TRACE; - False 285212672 - False TRACE; - False 4096 - True + true ..\..\Binaries\OpenTK\Release\ False False - False 4 - False - - AllRules.ruleset + none true @@ -184,6 +167,7 @@ + diff --git a/Source/Examples/ExampleBrowser.cs b/Source/Examples/ExampleBrowser.cs index 7d4bf59c..4e82d33e 100644 --- a/Source/Examples/ExampleBrowser.cs +++ b/Source/Examples/ExampleBrowser.cs @@ -42,10 +42,8 @@ namespace Examples #region Fields //PrivateFontCollection font_collection = new PrivateFontCollection(); - bool show_warning = true; - - static readonly string SourcePath = FindSourcePath(); + readonly string SourcePath; #endregion @@ -53,6 +51,10 @@ namespace Examples public ExampleBrowser() { + SourcePath = + FindSourcePath(Directory.GetCurrentDirectory()) ?? + FindSourcePath(Assembly.GetExecutingAssembly().Location); + Font = SystemFonts.DialogFont; InitializeComponent(); @@ -121,7 +123,8 @@ namespace Examples const string no_docs = "Documentation has not been entered."; const string no_source = "Source code has not been entered."; - if (e.Node.Tag != null && !String.IsNullOrEmpty(((ExampleInfo)e.Node.Tag).Attribute.Documentation)) + if (SourcePath != null && e.Node.Tag != null && + !String.IsNullOrEmpty(((ExampleInfo)e.Node.Tag).Attribute.Documentation)) { string docs = null; string source = null; @@ -358,7 +361,10 @@ namespace Examples MethodInfo main = e.Example.GetMethod("Main", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? - e.Example.GetMethod("Main", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(object), typeof(object) }, null); + e.Example.GetMethod("Main", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { + typeof(object), + typeof(object) + }, null); if (main != null) { try @@ -371,31 +377,13 @@ namespace Examples Trace.WriteLine(String.Format("Launching sample: \"{0}\"", e.Attribute.Title)); Trace.WriteLine(String.Empty); - Thread thread = new Thread((ThreadStart)delegate - { - try - { - main.Invoke(null, null); - } - catch (TargetInvocationException expt) - { - string ex_info; - if (expt.InnerException != null) - ex_info = expt.InnerException.ToString(); - else - ex_info = expt.ToString(); - MessageBox.Show(ex_info, "An OpenTK example encountered an error.", MessageBoxButtons.OK, MessageBoxIcon.Warning); + AppDomain sandbox = AppDomain.CreateDomain("Sandbox"); + sandbox.DomainUnload += HandleSandboxDomainUnload; - Debug.Print(expt.ToString()); - } - catch (NullReferenceException expt) - { - MessageBox.Show(expt.ToString(), "The Example launcher failed to load the example.", MessageBoxButtons.OK, MessageBoxIcon.Warning); - } - }); - thread.IsBackground = true; - thread.Start(); - thread.Join(); + SampleRunner runner = new SampleRunner(main); + CrossAppDomainDelegate cross = new CrossAppDomainDelegate(runner.Invoke); + sandbox.DoCallBack(cross); + AppDomain.Unload(sandbox); } finally { @@ -412,31 +400,68 @@ namespace Examples } } - // Tries to detect the path that contains the source for the examples. - static string FindSourcePath() + static void HandleSandboxDomainUnload(object sender, EventArgs e) { - string current_dir = Directory.GetCurrentDirectory(); + AppDomain sandbox = (AppDomain)sender; + sandbox.DomainUnload -= HandleSandboxDomainUnload; + } + + [Serializable] + class SampleRunner + { + MethodInfo _main; + + public SampleRunner(MethodInfo main) + { + _main = main; + } + + public void Invoke() + { + try + { + _main.Invoke(null, null); + } + catch (TargetInvocationException expt) + { + string ex_info; + if (expt.InnerException != null) + ex_info = expt.InnerException.ToString(); + else + ex_info = expt.ToString(); + //MessageBox.Show(ex_info, "An OpenTK example encountered an error.", MessageBoxButtons.OK, MessageBoxIcon.Warning); + + Trace.WriteLine(ex_info.ToString()); + } + catch (Exception expt) + { + Trace.WriteLine(expt.ToString()); + } + } + } + + // Tries to detect the path that contains the source for the examples. + static string FindSourcePath(string guess) + { + guess = Path.GetDirectoryName(guess); // Typically, our working directory is either "[opentk]/Binaries/OpenTK/[config]" or "[opentk]". // The desired source path is "[opentk]/Source/Examples/[ExampleCategory]" - - string guess = current_dir; if (CheckPath(ref guess)) return guess; // We were in [opentk] after all - guess = current_dir; for (int i = 0; i < 3; i++) { DirectoryInfo dir = Directory.GetParent(guess); - if (!dir.Exists) + if (dir == null || !dir.Exists) break; guess = dir.FullName; } - + if (CheckPath(ref guess)) return guess; // We were in [opentk]/Binaries/OpenTK/[config] after all - throw new DirectoryNotFoundException(); + return null; } static bool CheckPath(ref string path) diff --git a/Source/Examples/OpenGL/1.x/Anaglyph.cs b/Source/Examples/OpenGL/1.x/Anaglyph.cs new file mode 100644 index 00000000..b433987b --- /dev/null +++ b/Source/Examples/OpenGL/1.x/Anaglyph.cs @@ -0,0 +1,204 @@ +using System; +using System.Drawing; +using System.Collections.Generic; +using System.Diagnostics; + +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Graphics.OpenGL; +using OpenTK.Input; + +namespace Examples.Tutorial +{ + [Example( "Anaglyph Stereo", ExampleCategory.OpenGL, "1.x", Documentation = "Anaglyph" )] + + class Anaglyph : GameWindow + { + + Examples.Shapes.DrawableShape Object; + + /// Creates a 800x600 window with the specified title. + public Anaglyph() + : base(800, 600, GraphicsMode.Default, "OpenTK Quick Start Sample", GameWindowFlags.Default, DisplayDevice.Default, 3, 1, GraphicsContextFlags.Default) + { + VSync = VSyncMode.On; + } + + /// Load resources here. + /// Not used. + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + GL.ClearColor(System.Drawing.Color.Black); + GL.Enable(EnableCap.DepthTest); + + GL.Enable( EnableCap.Lighting ); + GL.Enable( EnableCap.Light0 ); + + Object = new Examples.Shapes.MengerSponge(1.0, Shapes.MengerSponge.eSubdivisions.Two, true ); + // Object = new Examples.Shapes.TorusKnot( 256, 32, 0.1, 3, 4, 1, true ); + } + + protected override void OnUnload( EventArgs e ) + { + base.OnUnload( e ); + + Object.Dispose(); + } + + /// + /// Called when your window is resized. Set your viewport here. It is also + /// a good place to set up your projection matrix (which probably changes + /// along when the aspect ratio of your window). + /// + /// Not used. + protected override void OnResize(EventArgs e) + { + base.OnResize(e); + + GL.Viewport(ClientRectangle); + } + + /// + /// Called when it is time to setup the next frame. Add you game logic here. + /// + /// Contains timing information for framerate independent logic. + protected override void OnUpdateFrame(FrameEventArgs e) + { + base.OnUpdateFrame(e); + + if (Keyboard[Key.Escape]) + Exit(); + } + + struct Camera + { + public Vector3 Position, Direction, Up; + public double NearPlane, FarPlane; + public double EyeSeparation; + public double Aperture; // FOV in degrees + public double FocalLength; + } + + enum Eye + { + left, + right, + } + + void SetupCamera( Eye eye ) + { + Camera camera; + + camera.Position = Vector3.UnitZ; + camera.Up = Vector3.UnitY; + camera.Direction = -Vector3.UnitZ; + camera.NearPlane = 1.0; + camera.FarPlane = 5.0; + camera.FocalLength = 2.0; + camera.EyeSeparation = camera.FocalLength / 30.0; + camera.Aperture = 75.0; + + double left, right, + bottom, top; + + double widthdiv2 = camera.NearPlane * Math.Tan( MathHelper.DegreesToRadians( (float)( camera.Aperture / 2.0 ) ) ); // aperture in radians + double precalc1 = ClientRectangle.Width / (double)ClientRectangle.Height * widthdiv2; + double precalc2 = 0.5 * camera.EyeSeparation * camera.NearPlane / camera.FocalLength; + + Vector3 Right = Vector3.Cross( camera.Direction, camera.Up ); // Each unit vectors + Right.Normalize(); + + Right.X *= (float)( camera.EyeSeparation / 2.0 ); + Right.Y *= (float)( camera.EyeSeparation / 2.0 ); + Right.Z *= (float)( camera.EyeSeparation / 2.0 ); + + // Projection Matrix + top = widthdiv2; + bottom = -widthdiv2; + if ( eye == Eye.right ) + { + left = -precalc1 - precalc2; + right = precalc1 - precalc2; + } + else + { + left = -precalc1 + precalc2; + right = precalc1 + precalc2; + } + + GL.MatrixMode( MatrixMode.Projection ); + GL.LoadIdentity(); + GL.Frustum( left, right, bottom, top, camera.NearPlane, camera.FarPlane ); + + // Modelview Matrix + Matrix4 modelview; + if ( eye == Eye.right ) + { + modelview = Matrix4.LookAt( + new Vector3( camera.Position.X + Right.X, camera.Position.Y + Right.Y, camera.Position.Z + Right.Z ), + new Vector3( camera.Position.X + Right.X + camera.Direction.X, camera.Position.Y + Right.Y + camera.Direction.Y, camera.Position.Z + Right.Z + camera.Direction.Z ), + camera.Up ); + } + else + { + modelview = Matrix4.LookAt( + new Vector3( camera.Position.X - Right.X, camera.Position.Y - Right.Y, camera.Position.Z - Right.Z ), + new Vector3( camera.Position.X - Right.X + camera.Direction.X, camera.Position.Y - Right.Y + camera.Direction.Y, camera.Position.Z - Right.Z + camera.Direction.Z ), + camera.Up ); + } + GL.MatrixMode( MatrixMode.Modelview ); + GL.LoadIdentity(); + GL.MultMatrix( ref modelview ); + + } + + float Angle; + + void Draw() + { + GL.Translate( 0f, 0f, -2f ); + GL.Rotate( Angle, Vector3.UnitY ); + Object.Draw(); + } + + /// + /// Called when it is time to render the next frame. Add your rendering code here. + /// + /// Contains timing information. + protected override void OnRenderFrame( FrameEventArgs e ) + { + Angle += (float)(e.Time *20.0); + + + GL.Clear( ClearBufferMask.DepthBufferBit | ClearBufferMask.ColorBufferBit ); + SetupCamera( Eye.right ); + GL.ColorMask( true, false, false, true ); + Draw(); + + GL.Clear( ClearBufferMask.DepthBufferBit ); // + SetupCamera( Eye.left ); + GL.ColorMask( false, true, true, true ); + Draw(); + + GL.ColorMask( true, true, true, true ); + SwapBuffers(); + } + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + // The 'using' idiom guarantees proper resource cleanup. + // We request 30 UpdateFrame events per second, and unlimited + // RenderFrame events (as fast as the computer can handle). + using (Anaglyph game = new Anaglyph()) + { + game.Run(10.0); + } + } + } +} \ No newline at end of file diff --git a/Source/Examples/OpenGL/1.x/Anaglyph.rtf b/Source/Examples/OpenGL/1.x/Anaglyph.rtf new file mode 100644 index 00000000..07ab7750 Binary files /dev/null and b/Source/Examples/OpenGL/1.x/Anaglyph.rtf differ diff --git a/Source/Examples/OpenGL/1.x/FramebufferObject.cs b/Source/Examples/OpenGL/1.x/FramebufferObject.cs index 69fd28da..886231cb 100644 --- a/Source/Examples/OpenGL/1.x/FramebufferObject.cs +++ b/Source/Examples/OpenGL/1.x/FramebufferObject.cs @@ -21,7 +21,7 @@ namespace Examples.Tutorial public class SimpleFBO : GameWindow { public SimpleFBO() - : base(800, 600) + : base(800, 400) { } @@ -33,24 +33,7 @@ namespace Examples.Tutorial const int TextureSize = 512; - #region Randoms - - Random rnd = new Random(); - public const float scale = 3f; - - /// Returns a random Float in the range [-0.5*scale..+0.5*scale] - public float GetRandom() - { - return (float)(rnd.NextDouble() - 0.5) * scale; - } - - /// Returns a random Float in the range [0..1] - public float GetRandom0to1() - { - return (float)rnd.NextDouble(); - } - - #endregion Randoms + Examples.Shapes.DrawableShape Object; protected override void OnLoad(EventArgs e) { @@ -63,13 +46,14 @@ namespace Examples.Tutorial Exit(); } + Object = new Shapes.TorusKnot(256, 16, 0.2, 7,8, 1, true); + GL.Enable(EnableCap.DepthTest); GL.ClearDepth(1.0f); GL.DepthFunc(DepthFunction.Lequal); - GL.Disable(EnableCap.CullFace); - GL.PolygonMode(MaterialFace.Back, PolygonMode.Line); - + GL.Enable(EnableCap.CullFace); + // Create Color Tex GL.GenTextures(1, out ColorTexture); GL.BindTexture(TextureTarget.Texture2D, ColorTexture); @@ -176,20 +160,24 @@ namespace Examples.Tutorial GL.ClearColor(1f, 0f, 0f, 0f); GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); - // smack 50 random triangles into the FBO's textures - GL.Begin(BeginMode.Triangles); - { - for (int i = 0; i < 50; i++) - { - GL.Color3(GetRandom0to1(), GetRandom0to1(), GetRandom0to1()); - GL.Vertex3(GetRandom(), GetRandom(), GetRandom()); - GL.Color3(GetRandom0to1(), GetRandom0to1(), GetRandom0to1()); - GL.Vertex3(GetRandom(), GetRandom(), GetRandom()); - GL.Color3(GetRandom0to1(), GetRandom0to1(), GetRandom0to1()); - GL.Vertex3(GetRandom(), GetRandom(), GetRandom()); - } - } - GL.End(); + OpenTK.Matrix4 perspective = OpenTK.Matrix4.CreatePerspectiveFieldOfView( MathHelper.PiOver4, TextureSize / (float)TextureSize, 2.5f, 6f ); + GL.MatrixMode( MatrixMode.Projection ); + GL.LoadMatrix( ref perspective ); + + Matrix4 lookat = Matrix4.LookAt( 0f, 0f, 4.5f, 0f, 0f, 0f, 0f, 1f, 0f ); + GL.MatrixMode( MatrixMode.Modelview ); + GL.LoadMatrix( ref lookat ); + + // draw some complex object into the FBO's textures + GL.Enable( EnableCap.Lighting ); + GL.Enable( EnableCap.Light0 ); + GL.Enable( EnableCap.ColorMaterial ); + GL.Color3( 0f, 1f, 0f ); + Object.Draw(); + GL.Disable( EnableCap.ColorMaterial ); + GL.Disable( EnableCap.Light0 ); + GL.Disable( EnableCap.Lighting ); + } GL.PopAttrib(); GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0); // disable rendering into the FBO @@ -203,6 +191,8 @@ namespace Examples.Tutorial protected override void OnUnload(EventArgs e) { + Object.Dispose(); + // Clean up what we allocated before exiting if (ColorTexture != 0) GL.DeleteTextures(1, ref ColorTexture); diff --git a/Source/Examples/OpenGL/1.x/FramebufferObject.rtf b/Source/Examples/OpenGL/1.x/FramebufferObject.rtf index 8d9e74f2..75ecb269 100644 Binary files a/Source/Examples/OpenGL/1.x/FramebufferObject.rtf and b/Source/Examples/OpenGL/1.x/FramebufferObject.rtf differ diff --git a/Source/Examples/OpenGL/1.x/OpenGLDiagnostics.cs b/Source/Examples/OpenGL/1.x/OpenGLDiagnostics.cs new file mode 100644 index 00000000..429f4d9e --- /dev/null +++ b/Source/Examples/OpenGL/1.x/OpenGLDiagnostics.cs @@ -0,0 +1,438 @@ +using System; +using System.Drawing; +using System.Collections.Generic; +using System.Diagnostics; + +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Graphics.OpenGL; +using OpenTK.Input; + +namespace Examples.Tutorial +{ + [Example( "OpenGL Diagnostics", ExampleCategory.OpenGL, "1.x", Documentation = "OpenGLDiagnostics" )] + + class GLDiagnostics : GameWindow + { + /// Creates a 800x600 window with the specified title. + public GLDiagnostics() + : base(80, 60, GraphicsMode.Default, "OpenTK Quick Start Sample", GameWindowFlags.Default, DisplayDevice.Default, 3, 1, GraphicsContextFlags.Default) + { + VSync = VSyncMode.On; + this.Context.ErrorChecking = false; + } + + struct TexFormat + { + public PixelInternalFormat pif; + public PixelFormat pf; + public PixelType pt; + + public TexFormat(PixelInternalFormat _pif, PixelFormat _pf, PixelType _pt) + { + pif = _pif; + pf = _pf; + pt = _pt; + } + } + + TexFormat[] TextureFormats = new TexFormat[] + { + new TexFormat( PixelInternalFormat.Alpha, PixelFormat.Alpha, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.Alpha4, PixelFormat.Alpha, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.Alpha8, PixelFormat.Alpha, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.Alpha12, PixelFormat.Alpha, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.Alpha16, PixelFormat.Alpha, PixelType.UnsignedByte), + new TexFormat( (PixelInternalFormat)All.Alpha16fArb, PixelFormat.Alpha, PixelType.HalfFloat), + new TexFormat( (PixelInternalFormat)All.Alpha32fArb, PixelFormat.Alpha, PixelType.Float), + + new TexFormat( PixelInternalFormat.DepthComponent, PixelFormat.DepthComponent, PixelType.Int), + new TexFormat( PixelInternalFormat.DepthComponent16, PixelFormat.DepthComponent, PixelType.Float), + new TexFormat( PixelInternalFormat.DepthComponent24, PixelFormat.DepthComponent, PixelType.Float), + new TexFormat( PixelInternalFormat.DepthComponent32, PixelFormat.DepthComponent, PixelType.Float), + new TexFormat( PixelInternalFormat.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float), + new TexFormat( PixelInternalFormat.DepthStencil, PixelFormat.DepthStencil, PixelType.UnsignedInt248), + new TexFormat( PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248), + new TexFormat( PixelInternalFormat.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev), + + new TexFormat( PixelInternalFormat.One, PixelFormat.Red, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.Two, PixelFormat.Rg, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.Rgb, PixelFormat.Rgb, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.Rgba, PixelFormat.Rgba, PixelType.UnsignedByte), + + new TexFormat( PixelInternalFormat.Srgb, PixelFormat.Rgb, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.SrgbAlpha, PixelFormat.Rgb, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.Srgb8, PixelFormat.Rgb, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.Srgb8Alpha8, PixelFormat.Rgba, PixelType.UnsignedByte), + + new TexFormat( PixelInternalFormat.R16f, PixelFormat.Red, PixelType.HalfFloat), + new TexFormat( PixelInternalFormat.Rg16f, PixelFormat.Rg, PixelType.HalfFloat), + new TexFormat( PixelInternalFormat.Rgb16f, PixelFormat.Rgb, PixelType.HalfFloat), + new TexFormat( PixelInternalFormat.Rgba16f, PixelFormat.Rgba, PixelType.HalfFloat), + new TexFormat( PixelInternalFormat.R32f, PixelFormat.Red, PixelType.Float), + new TexFormat( PixelInternalFormat.Rg32f, PixelFormat.Rg, PixelType.Float), + new TexFormat( PixelInternalFormat.Rgb32f, PixelFormat.Rgb, PixelType.Float), + new TexFormat( PixelInternalFormat.Rgba32f, PixelFormat.Rgba, PixelType.Float), + + new TexFormat( PixelInternalFormat.R8, PixelFormat.Red, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.Rg8, PixelFormat.Rg, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.Rgb8, PixelFormat.Rgb, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte), + + new TexFormat( PixelInternalFormat.R8ui, PixelFormat.Red, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.Rg8ui, PixelFormat.Rg, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.Rgb8ui, PixelFormat.Rgb, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.Rgba8ui, PixelFormat.Rgba, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.R16ui, PixelFormat.Red, PixelType.UnsignedShort), + new TexFormat( PixelInternalFormat.Rg16ui, PixelFormat.Rg, PixelType.UnsignedShort), + new TexFormat( PixelInternalFormat.Rgb16ui, PixelFormat.Rgb, PixelType.UnsignedShort), + new TexFormat( PixelInternalFormat.Rgba16ui, PixelFormat.Rgba, PixelType.UnsignedShort), + new TexFormat( PixelInternalFormat.R32ui, PixelFormat.Red, PixelType.UnsignedInt), + new TexFormat( PixelInternalFormat.Rg32ui, PixelFormat.Rg, PixelType.UnsignedInt), + new TexFormat( PixelInternalFormat.Rgb32ui, PixelFormat.Rgb, PixelType.UnsignedInt), + new TexFormat( PixelInternalFormat.Rgba32ui, PixelFormat.Rgba, PixelType.UnsignedInt), + + new TexFormat( PixelInternalFormat.R8i, PixelFormat.Red, PixelType.Byte), + new TexFormat( PixelInternalFormat.Rg8i, PixelFormat.Rg, PixelType.Byte), + new TexFormat( PixelInternalFormat.Rgb8i, PixelFormat.Rgb, PixelType.Byte), + new TexFormat( PixelInternalFormat.Rgba8i, PixelFormat.Rgba, PixelType.Byte), + new TexFormat( PixelInternalFormat.R16i, PixelFormat.Red, PixelType.Short), + new TexFormat( PixelInternalFormat.Rg16i, PixelFormat.Rg, PixelType.Short), + new TexFormat( PixelInternalFormat.Rgb16i, PixelFormat.Rgb, PixelType.Short), + new TexFormat( PixelInternalFormat.Rgba16i, PixelFormat.Rgba, PixelType.Short), + new TexFormat( PixelInternalFormat.R32i, PixelFormat.Red, PixelType.Int), + new TexFormat( PixelInternalFormat.Rg32i, PixelFormat.Rg, PixelType.Int), + new TexFormat( PixelInternalFormat.Rgb32i, PixelFormat.Rgb, PixelType.Int), + new TexFormat( PixelInternalFormat.Rgba32i, PixelFormat.Rgba, PixelType.Int), + + new TexFormat( PixelInternalFormat.R3G3B2, PixelFormat.Rgb, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.Rgb10A2, PixelFormat.Rgba, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.Rgb5A1, PixelFormat.Rgb, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.Rgb9E5, PixelFormat.Rgb, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.R11fG11fB10f, PixelFormat.Rgb, PixelType.UnsignedByte), + + new TexFormat( PixelInternalFormat.CompressedAlpha, PixelFormat.Alpha, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.CompressedIntensity, PixelFormat.Luminance, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.CompressedLuminance, PixelFormat.Luminance, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.CompressedLuminanceAlpha, PixelFormat.LuminanceAlpha, PixelType.UnsignedByte), + new TexFormat( (PixelInternalFormat)All.CompressedLuminanceLatc1Ext, PixelFormat.Luminance, PixelType.UnsignedByte), + new TexFormat( (PixelInternalFormat)All.CompressedLuminanceAlphaLatc2Ext, PixelFormat.LuminanceAlpha, PixelType.UnsignedByte), + + new TexFormat( PixelInternalFormat.CompressedRed, PixelFormat.Red, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.CompressedRedRgtc1, PixelFormat.Red, PixelType.UnsignedByte), + new TexFormat( (PixelInternalFormat)All.CompressedRedGreenRgtc2Ext, PixelFormat.Rg, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.CompressedRg, PixelFormat.Rg, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.CompressedRgRgtc2, PixelFormat.Rg, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.CompressedRgb, PixelFormat.Rgb, PixelType.UnsignedByte), + new TexFormat( (PixelInternalFormat)All.CompressedRgbFxt13Dfx, PixelFormat.Rgb, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.CompressedRgba, PixelFormat.Rgba, PixelType.UnsignedByte), + new TexFormat( (PixelInternalFormat)All.CompressedRgbaFxt13Dfx, PixelFormat.Rgba, PixelType.UnsignedByte), + + new TexFormat( (PixelInternalFormat)All.CompressedSignedLuminanceAlphaLatc2Ext, PixelFormat.LuminanceAlpha, PixelType.UnsignedByte), + new TexFormat( (PixelInternalFormat)All.CompressedSignedLuminanceLatc1Ext, PixelFormat.Luminance, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.CompressedSignedRedRgtc1, PixelFormat.Red, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.CompressedSignedRgRgtc2, PixelFormat.Rg, PixelType.UnsignedByte), + + new TexFormat( PixelInternalFormat.CompressedSluminance, PixelFormat.Luminance, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.CompressedSluminanceAlpha, PixelFormat.LuminanceAlpha, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.CompressedSrgb, PixelFormat.Rgb, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.CompressedSrgbAlpha, PixelFormat.Rgba, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.CompressedSrgbS3tcDxt1Ext, PixelFormat.Rgb, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.CompressedSrgbAlphaS3tcDxt1Ext, PixelFormat.Rgba, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.CompressedSrgbAlphaS3tcDxt3Ext, PixelFormat.Rgba, PixelType.UnsignedByte), + new TexFormat( PixelInternalFormat.CompressedSrgbAlphaS3tcDxt5Ext, PixelFormat.Rgba, PixelType.UnsignedByte), + + new TexFormat( (PixelInternalFormat)All.CompressedRgbS3tcDxt1Ext, PixelFormat.Rgb, PixelType.UnsignedByte), + new TexFormat( (PixelInternalFormat)All.CompressedRgbaS3tcDxt1Ext, PixelFormat.Rgba, PixelType.UnsignedByte), + new TexFormat( (PixelInternalFormat)All.CompressedRgbaS3tcDxt3Ext, PixelFormat.Rgba, PixelType.UnsignedByte), + new TexFormat( (PixelInternalFormat)All.CompressedRgbaS3tcDxt5Ext, PixelFormat.Rgba, PixelType.UnsignedByte), + + }; + + #region GL.Get* Helper + + public enum eType + { + Boolean, + Int, + IntEnum, + IntArray2, + IntArray4, + Float, + FloatArray2, + FloatArray4, + } + + public void Analyze(GetPName pname, eType type) + { + bool result1b; + int result1i; + int[] result2i = new int[2]; + int[] result4i = new int[4]; + float result1f; + Vector2 result2f; + Vector4 result4f; + string output; + + switch (type) + { + case eType.Boolean: + GL.GetBoolean(pname, out result1b); + output = pname + ": " + result1b; + break; + case eType.Int: + GL.GetInteger(pname, out result1i); + output = pname + ": " + result1i; + break; + case eType.IntEnum: + GL.GetInteger(pname, out result1i); + output = pname + ": " + (All)result1i; + break; + case eType.IntArray2: + GL.GetInteger(pname, result2i); + output = pname + ": ( " + result2i[0] + ", " + result2i[1] + " )"; + break; + case eType.IntArray4: + GL.GetInteger(pname, result4i); + output = pname + ": ( " + result4i[0] + ", " + result4i[1] + " ) ( " + result4i[2] + ", " + result4i[3] + " )"; + break; + case eType.Float: + GL.GetFloat(pname, out result1f); + output = pname + ": " + result1f; + break; + case eType.FloatArray2: + GL.GetFloat(pname, out result2f); + output = pname + ": ( " + result2f.X + ", " + result2f.Y + " )"; + break; + case eType.FloatArray4: + GL.GetFloat(pname, out result4f); + output = pname + ": ( " + result4f.X + ", " + result4f.Y + ", " + result4f.Z + ", " + result4f.W + " )"; + break; + default: throw new NotImplementedException(); + } + + ErrorCode err = GL.GetError(); + if (err != ErrorCode.NoError) + Trace.WriteLine("Unsupported Token: " + pname); + else + Trace.WriteLine(output); + + } + + #endregion GL.Get* Helper + + /// Load resources here. + /// Not used. + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + GL.ClearColor(System.Drawing.Color.MidnightBlue); + GL.Enable(EnableCap.DepthTest); + + // returns 0 formats, driver broken? + /* + int CompressedTextureFormatCount; + GL.GetInteger(GetPName.NumCompressedTextureFormats, out CompressedTextureFormatCount); + if (CompressedTextureFormatCount > 0) + { + int[] CompressedTextureFormats = new int[CompressedTextureFormatCount]; + GL.GetInteger(GetPName.CompressedTextureFormats, CompressedTextureFormats); + Trace.WriteLine("Supported compressed Texture formats:"); + for (int i = 0; i < CompressedTextureFormats.Length; i++) + Trace.Write((All)CompressedTextureFormats[i] + ", "); + } + */ + + string Renderer = GL.GetString(StringName.Renderer); + string GLSLang = GL.GetString(StringName.ShadingLanguageVersion); + string Vendor = GL.GetString(StringName.Vendor); + string Version = GL.GetString(StringName.Version); + + string ExtensionsRaw = GL.GetString(StringName.Extensions); + string[] splitter = new string[] { " " }; + string[] Extensions = ExtensionsRaw.Split(splitter, StringSplitOptions.None); + + Trace.WriteLine("Vendor: " + Vendor); + Trace.WriteLine("Renderer: " + Renderer); + Trace.WriteLine("GL Version: " + Version); + Analyze(GetPName.MajorVersion, eType.Int); + Analyze(GetPName.MinorVersion, eType.Int); + Trace.WriteLine("GLSL Version: " + GLSLang); + Trace.WriteLine("Extensions: "); + for (int i = 0; i < Extensions.Length; i++) + Trace.WriteLine(Extensions[i]); + + Trace.WriteLine("--- Framebuffer ---"); + Analyze(GetPName.Doublebuffer, eType.Boolean); + Analyze(GetPName.MaxColorAttachments, eType.Int); + Analyze(GetPName.MaxDrawBuffers, eType.Int); + Analyze(GetPName.AuxBuffers, eType.Int); + Analyze(GetPName.DrawBuffer, eType.IntEnum); + Analyze(GetPName.MaxSamples, eType.Int); + Analyze(GetPName.MaxViewportDims, eType.IntArray2); + Analyze(GetPName.Viewport, eType.IntArray4); + + Trace.WriteLine("--- Framebuffer channels ---"); + Analyze(GetPName.RedBits, eType.Int); + Analyze(GetPName.GreenBits, eType.Int); + Analyze(GetPName.BlueBits, eType.Int); + Analyze(GetPName.AlphaBits, eType.Int); + Analyze(GetPName.DepthBits, eType.Int); + Analyze(GetPName.StencilBits, eType.Int); + + Analyze(GetPName.AccumRedBits, eType.Int); + Analyze(GetPName.AccumGreenBits, eType.Int); + Analyze(GetPName.AccumBlueBits, eType.Int); + Analyze(GetPName.AccumAlphaBits, eType.Int); + + Trace.WriteLine("--- Textures ---"); + Analyze(GetPName.MaxCombinedTextureImageUnits, eType.Int); + Analyze(GetPName.MaxVertexTextureImageUnits, eType.Int); + Analyze(GetPName.MaxTextureImageUnits, eType.Int); + Analyze(GetPName.MaxTextureUnits, eType.Int); + Analyze(GetPName.MaxTextureSize, eType.Int); + Analyze(GetPName.Max3DTextureSize, eType.Int); + Analyze(GetPName.MaxCubeMapTextureSize, eType.Int); + Analyze(GetPName.MaxRenderbufferSize, eType.Int); + Analyze(GetPName.MaxTextureLodBias, eType.Int); + + Queue Supported = new Queue(); + Queue Unsupported = new Queue(); + + uint DummyTexture; + foreach (TexFormat t in TextureFormats) + { + GL.GenTextures(1, out DummyTexture); + GL.BindTexture(TextureTarget.Texture2D, DummyTexture); + GL.TexImage2D(TextureTarget.Texture2D, 0, t.pif, 4, 4, 0, t.pf, t.pt, IntPtr.Zero); + if (GL.GetError() == ErrorCode.NoError) + Supported.Enqueue(t); + else + Unsupported.Enqueue(t); + GL.DeleteTextures(1, ref DummyTexture); + } + GL.BindTexture(TextureTarget.Texture2D, 0); + + Trace.WriteLine("--- UN-supported Texture formats ---"); + while (Unsupported.Count > 0) + { + TexFormat tex = Unsupported.Dequeue(); + Trace.Write((All)tex.pif+", "); + } + Trace.WriteLine( " " ); + + Trace.WriteLine("--- SUPPORTED Texture formats ---"); + while (Supported.Count > 0) + { + TexFormat tex = Supported.Dequeue(); + Trace.WriteLine((All)tex.pif+" " +tex.pf + " "+tex.pt); + } + Trace.WriteLine(" "); + + Trace.WriteLine("--- Point&Line volumes ---"); + Analyze(GetPName.AliasedPointSizeRange, eType.FloatArray2); + Analyze(GetPName.PointSizeMin, eType.Float); + Analyze(GetPName.PointSizeMax, eType.Float); + Analyze(GetPName.PointSizeGranularity, eType.Float); + Analyze(GetPName.PointSizeRange, eType.FloatArray2); + + Analyze(GetPName.AliasedLineWidthRange, eType.FloatArray2); + Analyze(GetPName.LineWidthGranularity, eType.Float); + Analyze(GetPName.LineWidthRange, eType.FloatArray2); + + Trace.WriteLine("--- VBO ---"); + Analyze(GetPName.MaxElementsIndices, eType.Int); + Analyze(GetPName.MaxElementsVertices, eType.Int); + Analyze(GetPName.MaxVertexAttribs, eType.Int); + + Trace.WriteLine("--- GLSL ---"); + Analyze(GetPName.MaxCombinedFragmentUniformComponents, eType.Int); + Analyze(GetPName.MaxCombinedGeometryUniformComponents, eType.Int); + Analyze(GetPName.MaxCombinedVertexUniformComponents, eType.Int); + Analyze(GetPName.MaxFragmentUniformComponents, eType.Int); + Analyze(GetPName.MaxVertexUniformComponents, eType.Int); + + Analyze(GetPName.MaxCombinedUniformBlocks, eType.Int); + Analyze(GetPName.MaxFragmentUniformBlocks, eType.Int); + Analyze(GetPName.MaxGeometryUniformBlocks, eType.Int); + Analyze(GetPName.MaxVertexUniformBlocks, eType.Int); + Analyze(GetPName.MaxUniformBlockSize, eType.Int); + Analyze(GetPName.MaxUniformBufferBindings, eType.Int); + + Analyze(GetPName.MaxVaryingFloats, eType.Int); + + Trace.WriteLine("--- Transform Feedback ---"); + Analyze(GetPName.MaxTransformFeedbackInterleavedComponents, eType.Int); + Analyze(GetPName.MaxTransformFeedbackSeparateAttribs, eType.Int); + Analyze(GetPName.MaxTransformFeedbackSeparateComponents, eType.Int); + + Trace.WriteLine("--- Fixed-Func Stacks, GL.Push* and GL.Pop* ---"); + Analyze(GetPName.MaxClientAttribStackDepth, eType.Int); + Analyze(GetPName.MaxAttribStackDepth, eType.Int); + Analyze(GetPName.MaxProjectionStackDepth, eType.Int); + Analyze(GetPName.MaxModelviewStackDepth, eType.Int); + Analyze(GetPName.MaxTextureStackDepth, eType.Int); + Analyze(GetPName.MaxNameStackDepth, eType.Int); + + Trace.WriteLine("--- Fixed-Func misc. stuff ---"); + Analyze(GetPName.MaxEvalOrder, eType.Int); + Analyze(GetPName.MaxClipPlanes, eType.Int); + Analyze(GetPName.MaxArrayTextureLayers, eType.Int); + Analyze(GetPName.MaxListNesting, eType.Int); + Analyze(GetPName.MaxLights, eType.Int); + Analyze(GetPName.MaxTextureCoords, eType.Int); + + this.Exit(); + } + + /// + /// Called when your window is resized. Set your viewport here. It is also + /// a good place to set up your projection matrix (which probably changes + /// along when the aspect ratio of your window). + /// + /// Not used. + protected override void OnResize(EventArgs e) + { + base.OnResize(e); + + GL.Viewport(ClientRectangle); + } + + /// + /// Called when it is time to setup the next frame. Add you game logic here. + /// + /// Contains timing information for framerate independent logic. + protected override void OnUpdateFrame(FrameEventArgs e) + { + base.OnUpdateFrame(e); + + if (Keyboard[Key.Escape]) + Exit(); + } + + /// + /// Called when it is time to render the next frame. Add your rendering code here. + /// + /// Contains timing information. + protected override void OnRenderFrame(FrameEventArgs e) + { + base.OnRenderFrame(e); + } + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + // The 'using' idiom guarantees proper resource cleanup. + // We request 30 UpdateFrame events per second, and unlimited + // RenderFrame events (as fast as the computer can handle). + using (GLDiagnostics game = new GLDiagnostics()) + { + game.Run(10.0); + } + } + } +} \ No newline at end of file diff --git a/Source/Examples/OpenGL/1.x/OpenGLDiagnostics.rtf b/Source/Examples/OpenGL/1.x/OpenGLDiagnostics.rtf new file mode 100644 index 00000000..662fae65 Binary files /dev/null and b/Source/Examples/OpenGL/1.x/OpenGLDiagnostics.rtf differ diff --git a/Source/Examples/OpenGL/1.x/VBODynamic.cs b/Source/Examples/OpenGL/1.x/VBODynamic.cs index 72d6bacc..d1831634 100644 --- a/Source/Examples/OpenGL/1.x/VBODynamic.cs +++ b/Source/Examples/OpenGL/1.x/VBODynamic.cs @@ -57,8 +57,8 @@ namespace Examples.Tutorial GL.Hint(HintTarget.PointSmoothHint, HintMode.Nicest); // Setup VBO state - GL.EnableClientState(EnableCap.ColorArray); - GL.EnableClientState(EnableCap.VertexArray); + GL.EnableClientState(ArrayCap.ColorArray); + GL.EnableClientState(ArrayCap.VertexArray); GL.GenBuffers(1, out VBOHandle); diff --git a/Source/Examples/OpenGL/2.x/GeometryShaderAdvanced.cs b/Source/Examples/OpenGL/2.x/GeometryShaderAdvanced.cs index 8935d340..c0457bcd 100644 --- a/Source/Examples/OpenGL/2.x/GeometryShaderAdvanced.cs +++ b/Source/Examples/OpenGL/2.x/GeometryShaderAdvanced.cs @@ -730,9 +730,9 @@ namespace Examples.Tutorial //GL.Arb.DrawArraysInstanced(BeginMode.Triangles, 0, cubeData.Length/8, 1); GL.DrawArrays(BeginMode.Triangles, 0, cubeData.Length / (vboCubeStride / sizeof(float))); - GL.DisableClientState(EnableCap.VertexArray); - GL.DisableClientState(EnableCap.NormalArray); - GL.DisableClientState(EnableCap.TextureCoordArray); + GL.DisableClientState(ArrayCap.VertexArray); + GL.DisableClientState(ArrayCap.NormalArray); + GL.DisableClientState(ArrayCap.TextureCoordArray); } void renderSphereVBO() @@ -753,9 +753,9 @@ namespace Examples.Tutorial //GL.Arb.DrawArraysInstanced(BeginMode.Triangles, 0, cubeData.Length/8, 1); //GL.DrawArrays(BeginMode.Triangles, 0, sphereData.Length / (vboSphereStride / sizeof(float))); - GL.DisableClientState(EnableCap.VertexArray); - GL.DisableClientState(EnableCap.NormalArray); - GL.DisableClientState(EnableCap.TextureCoordArray); + GL.DisableClientState(ArrayCap.VertexArray); + GL.DisableClientState(ArrayCap.NormalArray); + GL.DisableClientState(ArrayCap.TextureCoordArray); } void renderCubemap() diff --git a/Source/Examples/OpenGL/2.x/SimpleGLSL.cs b/Source/Examples/OpenGL/2.x/SimpleGLSL.cs index 8d55db3d..319ac663 100644 --- a/Source/Examples/OpenGL/2.x/SimpleGLSL.cs +++ b/Source/Examples/OpenGL/2.x/SimpleGLSL.cs @@ -261,8 +261,8 @@ namespace Examples.Tutorial //GL.DrawArrays(GL.Enums.BeginMode.POINTS, 0, shape.Vertices.Length); - GL.DisableClientState(EnableCap.VertexArray); - GL.DisableClientState(EnableCap.ColorArray); + GL.DisableClientState(ArrayCap.VertexArray); + GL.DisableClientState(ArrayCap.ColorArray); //int error = GL.GetError(); diff --git a/Source/Examples/OpenTK.Examples.csproj b/Source/Examples/OpenTK.Examples.csproj index 3fcfa3a5..789042bb 100644 --- a/Source/Examples/OpenTK.Examples.csproj +++ b/Source/Examples/OpenTK.Examples.csproj @@ -1,4 +1,4 @@ - + Local @@ -80,6 +80,10 @@ ..\..\Binaries\OpenTK\Release\ + none + 4 + true + TRACE; true @@ -143,6 +147,10 @@ Code + + + Code + Code @@ -543,6 +551,8 @@ Always + + diff --git a/Source/Examples/OpenTK/GLControl/GLControlGameLoop.cs b/Source/Examples/OpenTK/GLControl/GLControlGameLoop.cs index 816fb039..66b5bff5 100644 --- a/Source/Examples/OpenTK/GLControl/GLControlGameLoop.cs +++ b/Source/Examples/OpenTK/GLControl/GLControlGameLoop.cs @@ -66,7 +66,9 @@ namespace Examples.WinForms void glControl_KeyUp(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.F12) - glControl.GrabScreenshot().Save("screenshot.png"); + { + GrabScreenshot().Save("screenshot.png"); + } } #endregion @@ -204,6 +206,23 @@ namespace Examples.WinForms #endregion + #region private void GrabScreenshot() + + Bitmap GrabScreenshot() + { + Bitmap bmp = new Bitmap(this.ClientSize.Width, this.ClientSize.Height); + System.Drawing.Imaging.BitmapData data = + bmp.LockBits(this.ClientRectangle, System.Drawing.Imaging.ImageLockMode.WriteOnly, + System.Drawing.Imaging.PixelFormat.Format24bppRgb); + GL.ReadPixels(0, 0, this.ClientSize.Width, this.ClientSize.Height, PixelFormat.Bgr, PixelType.UnsignedByte, + data.Scan0); + bmp.UnlockBits(data); + bmp.RotateFlip(RotateFlipType.RotateNoneFlipY); + return bmp; + } + + #endregion + #region public static void Main() /// diff --git a/Source/Examples/OpenTK/Test/BlittableValueTypes.cs b/Source/Examples/OpenTK/Test/BlittableValueTypes.cs index 3b0fab6e..2f7f2b15 100644 --- a/Source/Examples/OpenTK/Test/BlittableValueTypes.cs +++ b/Source/Examples/OpenTK/Test/BlittableValueTypes.cs @@ -1,44 +1,46 @@ // This code is in the Public Domain. It is provided "as is" -// without express or implied warranty of any kind. - -using System; -using System.Diagnostics; -using System.Reflection; -using OpenTK; - -namespace Examples.Tests -{ - struct Simple { public int Value; } - struct Generic { public T Value; } - enum Enum { First, Second } - struct Complex { public Simple Value; } - struct Complex { public Generic Value; } - struct Complex2 { public Enum Value; } - struct Complex3 { public Class Value; } - struct Complex4 : Interface { public Class Value; } - class Class { public int Value; } - class Class { public T Value; } - interface Interface { } - - [Example("Blittable Value Types", ExampleCategory.OpenTK, "Test", Documentation="BlittableValueTypes")] - public class BlittableValueTypes - { - public static void Main() - { - TestType(new Simple()); - TestType(new Generic()); - TestType(new Generic()); - TestType(new Complex()); - TestType(new Complex()); - TestType(new Complex2()); - TestType(new Complex3()); - TestType(new Complex4()); - TestType(new Class()); - TestType(new Class()); - } - - // Tests whether specified type is blittable and prints its marshalled size if so. - static void TestType(T instance) +// without express or implied warranty of any kind. + +using System; +using System.Diagnostics; +using System.Reflection; +using OpenTK; + +#pragma warning disable 0649 // Do not warn about unitialized fields, this is on purpose + +namespace Examples.Tests +{ + struct Simple { public int Value; } + struct Generic { public T Value; } + enum Enum { First, Second } + struct Complex { public Simple Value; } + struct Complex { public Generic Value; } + struct Complex2 { public Enum Value; } + struct Complex3 { public Class Value; } + struct Complex4 : Interface { public Class Value; } + class Class { public int Value; } + class Class { public T Value; } + interface Interface { } + + [Example("Blittable Value Types", ExampleCategory.OpenTK, "Test", Documentation="BlittableValueTypes")] + public class BlittableValueTypes + { + public static void Main() + { + TestType(new Simple()); + TestType(new Generic()); + TestType(new Generic()); + TestType(new Complex()); + TestType(new Complex()); + TestType(new Complex2()); + TestType(new Complex3()); + TestType(new Complex4()); + TestType(new Class()); + TestType(new Class()); + } + + // Tests whether specified type is blittable and prints its marshalled size if so. + static void TestType(T instance) { PrintType(); @@ -52,9 +54,9 @@ namespace Examples.Tests catch (Exception e) { Trace.Write(String.Format("({0})", e.GetType().Name)); - } - - Trace.WriteLine(""); + } + + Trace.WriteLine(""); } // Prints a simple description for the type. @@ -71,6 +73,6 @@ namespace Examples.Tests Trace.Write(typename.Substring(typename.LastIndexOf('.') + 1)); Trace.Write(" } "); - } - } -} + } + } +} diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index c15c0c21..33c42000 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -14,15 +14,17 @@ using OpenTK.Input; namespace Examples.Tests { - [Example("GameWindow states", ExampleCategory.OpenTK, "Test", Documentation = "GameWindowStates")] + [Example("GameWindow States", ExampleCategory.OpenTK, "GameWindow", 4, Documentation = "GameWindowStates")] public class GameWindowStates : GameWindow { - static readonly Font TextFont = new Font(FontFamily.GenericSansSerif, 12); + static readonly Font TextFont = new Font(FontFamily.GenericSansSerif, 11); Bitmap TextBitmap = new Bitmap(512, 512); int texture; bool mouse_in_window = false; bool viewport_changed = true; bool refresh_text = true; + MouseState mouse, mouse_old; + KeyboardState keyboard, keyboard_old; public GameWindowStates() : base(800, 600) @@ -38,14 +40,22 @@ namespace Examples.Tests Resize += delegate { refresh_text = true; }; WindowBorderChanged += delegate { refresh_text = true; }; WindowStateChanged += delegate { refresh_text = true; }; - Mouse.Move += delegate { refresh_text = true; }; + FocusedChanged += delegate { refresh_text = true; }; + Mouse.Move += MouseMoveHandler; + Mouse.ButtonDown += MouseButtonHandler; + Mouse.ButtonUp += MouseButtonHandler; } void KeyDownHandler(object sender, KeyboardKeyEventArgs e) { switch (e.Key) { - case OpenTK.Input.Key.Escape: this.Exit(); break; + case OpenTK.Input.Key.Escape: + if (!CursorVisible) + CursorVisible = true; + else + Exit(); + break; case Key.Number1: WindowState = WindowState.Normal; break; case Key.Number2: WindowState = WindowState.Maximized; break; @@ -68,7 +78,22 @@ namespace Examples.Tests case Key.Minus: Size -= new Size(16, 16); 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; + } + } + static int Clamp(int val, int min, int max) { return val > max ? max : val < min ? min : val; @@ -79,8 +104,58 @@ namespace Examples.Tests gfx.DrawString(str, TextFont, Brushes.White, new PointF(0, line * TextFont.Height)); } + static void DrawString(Graphics gfx, string str, int line, float offset) + { + gfx.DrawString(str, TextFont, Brushes.White, new PointF(offset, line * TextFont.Height)); + } + + static void DrawKeyboard(Graphics gfx, KeyboardState keyboard, 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++) + { + Key k = (Key)i; + if (keyboard[k]) + { + string key = k.ToString(); + DrawString(gfx, key, line, offset); + offset += gfx.MeasureString(key, TextFont).Width + space; + } + } + } + + static void DrawMouse(Graphics gfx, MouseState mouse, 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++) + { + MouseButton b = (MouseButton)i; + if (mouse[b]) + { + string button = b.ToString(); + DrawString(gfx, button, line, offset); + offset += gfx.MeasureString(button, TextFont).Width + space; + } + } + } + protected override void OnUpdateFrame(FrameEventArgs e) { + mouse = OpenTK.Input.Mouse.GetState(); + if (mouse != mouse_old) + refresh_text = true; + mouse_old = mouse; + keyboard = OpenTK.Input.Keyboard.GetState(); + if (keyboard != keyboard_old) + refresh_text = true; + keyboard_old = keyboard; + if (refresh_text) { refresh_text = false; @@ -89,18 +164,22 @@ namespace Examples.Tests { int line = 0; - gfx.Clear(Color.MidnightBlue); + gfx.Clear(Color.Black); gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; 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 position: {0}", new Vector3(Mouse.X, Mouse.Y, Mouse.Wheel)), 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++); + DrawKeyboard(gfx, keyboard, line++); + DrawMouse(gfx, mouse, line++); } } @@ -120,6 +199,8 @@ namespace Examples.Tests GL.ClearColor(Color.MidnightBlue); GL.Enable(EnableCap.Texture2D); + GL.Enable(EnableCap.Blend); + GL.BlendFunc(BlendingFactorSrc.One, BlendingFactorDest.OneMinusSrcColor); texture = GL.GenTexture(); GL.BindTexture(TextureTarget.Texture2D, texture); diff --git a/Source/GLControl/OpenTK.GLControl.csproj b/Source/GLControl/OpenTK.GLControl.csproj index f2e53c94..1c41c469 100644 --- a/Source/GLControl/OpenTK.GLControl.csproj +++ b/Source/GLControl/OpenTK.GLControl.csproj @@ -1,4 +1,4 @@ - + Local @@ -76,6 +76,10 @@ ..\..\Binaries\OpenTK\Release\ + none + 4 + true + TRACE; true diff --git a/Source/OpenTK/Audio/OpenAL/AL/AL.cs b/Source/OpenTK/Audio/OpenAL/AL/AL.cs index 295756d0..39191aa8 100644 --- a/Source/OpenTK/Audio/OpenAL/AL/AL.cs +++ b/Source/OpenTK/Audio/OpenAL/AL/AL.cs @@ -1274,13 +1274,36 @@ namespace OpenTK.Audio.OpenAL /// Pointer to an array of uint values which will store the names of the new buffers. [CLSCompliant(false)] [DllImport(AL.Lib, EntryPoint = "alGenBuffers", ExactSpelling = true, CallingConvention = AL.Style), SuppressUnmanagedCodeSecurity] - public static extern void GenBuffers(int n, out uint buffers); + unsafe public static extern void GenBuffers(int n, [Out] int* buffers); /// This function generates one or more buffers, which contain audio buffer (see AL.BufferData). References to buffers are uint values, which are used wherever a buffer reference is needed (in calls such as AL.DeleteBuffers, AL.Source with parameter ALSourcei, AL.SourceQueueBuffers, and AL.SourceUnqueueBuffers). /// The number of buffers to be generated. /// Pointer to an array of uint values which will store the names of the new buffers. - [DllImport(AL.Lib, EntryPoint = "alGenBuffers", ExactSpelling = true, CallingConvention = AL.Style), SuppressUnmanagedCodeSecurity] - public static extern void GenBuffers(int n, out int buffers); + [CLSCompliant(false)] + public static void GenBuffers(int n, out uint buffers) + { + unsafe + { + fixed (uint* pbuffers = &buffers) + { + GenBuffers(n, pbuffers); + } + } + } + + /// This function generates one or more buffers, which contain audio buffer (see AL.BufferData). References to buffers are uint values, which are used wherever a buffer reference is needed (in calls such as AL.DeleteBuffers, AL.Source with parameter ALSourcei, AL.SourceQueueBuffers, and AL.SourceUnqueueBuffers). + /// The number of buffers to be generated. + /// Pointer to an array of uint values which will store the names of the new buffers. + public static void GenBuffers(int n, out int buffers) + { + unsafe + { + fixed (int* pbuffers = &buffers) + { + GenBuffers(n, pbuffers); + } + } + } /// This function generates one or more buffers, which contain audio data (see AL.BufferData). References to buffers are uint values, which are used wherever a buffer reference is needed (in calls such as AL.DeleteBuffers, AL.Source with parameter ALSourcei, AL.SourceQueueBuffers, and AL.SourceUnqueueBuffers). /// The number of buffers to be generated. @@ -1326,13 +1349,36 @@ namespace OpenTK.Audio.OpenAL /// Pointer to an array of buffer names identifying the buffers to be deleted. [CLSCompliant(false)] [DllImport(AL.Lib, EntryPoint = "alDeleteBuffers", ExactSpelling = true, CallingConvention = AL.Style), SuppressUnmanagedCodeSecurity()] - public static extern void DeleteBuffers(int n, [In] ref uint buffers); + unsafe public static extern void DeleteBuffers(int n, [In] int* buffers); /// This function deletes one or more buffers, freeing the resources used by the buffer. Buffers which are attached to a source can not be deleted. See AL.Source (ALSourcei) and AL.SourceUnqueueBuffers for information on how to detach a buffer from a source. /// The number of buffers to be deleted. /// Pointer to an array of buffer names identifying the buffers to be deleted. - [DllImport(AL.Lib, EntryPoint = "alDeleteBuffers", ExactSpelling = true, CallingConvention = AL.Style), SuppressUnmanagedCodeSecurity()] - public static extern void DeleteBuffers(int n, [In] ref int buffers); + [CLSCompliant(false)] + public static void DeleteBuffers(int n, [In] ref uint buffers) + { + unsafe + { + fixed (uint* pbuffers = &buffers) + { + DeleteBuffers(n, pbuffers); + } + } + } + + /// This function deletes one or more buffers, freeing the resources used by the buffer. Buffers which are attached to a source can not be deleted. See AL.Source (ALSourcei) and AL.SourceUnqueueBuffers for information on how to detach a buffer from a source. + /// The number of buffers to be deleted. + /// Pointer to an array of buffer names identifying the buffers to be deleted. + public static void DeleteBuffers(int n, [In] ref int buffers) + { + unsafe + { + fixed (int* pbuffers = &buffers) + { + DeleteBuffers(n, pbuffers); + } + } + } /// This function deletes one buffer only, freeing the resources used by the buffer. Buffers which are attached to a source can not be deleted. See AL.Source (ALSourcei) and AL.SourceUnqueueBuffers for information on how to detach a buffer from a source. /// Pointer to a buffer name identifying the buffer to be deleted. diff --git a/Source/OpenTK/DisplayDevice.cs b/Source/OpenTK/DisplayDevice.cs index 5a20ba35..a95cb19a 100644 --- a/Source/OpenTK/DisplayDevice.cs +++ b/Source/OpenTK/DisplayDevice.cs @@ -1,16 +1,33 @@ -#region --- License --- -/* Licensed under the MIT/X11 license. - * Copyright (c) 2006-2008 the OpenTK team. - * This notice may not be removed. - * See license.txt for licensing detailed licensing details. - */ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 using System; using System.Collections.Generic; -using System.Text; using System.Diagnostics; -using System.Threading; using System.Drawing; namespace OpenTK @@ -21,23 +38,19 @@ namespace OpenTK /// public class DisplayDevice { - // TODO: Add support for refresh rate queries and switches. - // TODO: Check whether bits_per_pixel works correctly under Mono/X11. // TODO: Add properties that describe the 'usable' size of the Display, i.e. the maximized size without the taskbar etc. // TODO: Does not detect changes to primary device. - // TODO: Mono does not support System.Windows.Forms.Screen.BitsPerPixel -- find workaround! - #region --- Fields --- + #region Fields + bool primary; + Rectangle bounds; DisplayResolution current_resolution = new DisplayResolution(), original_resolution; List available_resolutions = new List(); IList available_resolutions_readonly; - bool primary; + + internal object Id; // A platform-specific id for this monitor - Rectangle bounds; - - static readonly List available_displays = new List(); - static readonly IList available_displays_readonly; static readonly object display_lock = new object(); static DisplayDevice primary_display; @@ -45,26 +58,21 @@ namespace OpenTK #endregion - #region --- Constructors --- + #region Constructors static DisplayDevice() { implementation = Platform.Factory.Default.CreateDisplayDeviceDriver(); - available_displays_readonly = available_displays.AsReadOnly(); } internal DisplayDevice() { - lock (display_lock) - { - available_displays.Add(this); - } - available_resolutions_readonly = available_resolutions.AsReadOnly(); } internal DisplayDevice(DisplayResolution currentResolution, bool primary, - IEnumerable availableResolutions, Rectangle bounds) + IEnumerable availableResolutions, Rectangle bounds, + object id) : this() { #warning "Consolidate current resolution with bounds? Can they fall out of sync right now?" @@ -72,9 +80,7 @@ namespace OpenTK IsPrimary = primary; this.available_resolutions.AddRange(availableResolutions); this.bounds = bounds == Rectangle.Empty ? currentResolution.Bounds : bounds; - - Debug.Print("DisplayDevice {0} ({1}) supports {2} resolutions.", - available_displays.Count, primary ? "primary" : "secondary", available_resolutions.Count); + this.Id = id; } #endregion @@ -280,10 +286,23 @@ namespace OpenTK /// /// Gets the list of available objects. + /// This function allocates memory. /// + [Obsolete("Use GetDisplay(DisplayIndex) instead.")] public static IList AvailableDisplays { - get { return available_displays_readonly; } + get + { + List displays = new List(); + for (int i = 0; i < 6; i++) + { + DisplayDevice dev = GetDisplay(DisplayIndex.First + i); + if (dev != null) + displays.Add(dev); + } + + return displays.AsReadOnly(); + } } #endregion @@ -291,7 +310,24 @@ namespace OpenTK #region public static DisplayDevice Default /// Gets the default (primary) display of this system. - public static DisplayDevice Default { get { return primary_display; } } + public static DisplayDevice Default + { + get { return implementation.GetDisplay(DisplayIndex.Primary); } + } + + #endregion + + #region GetDisplay + + /// + /// Gets the for the specified . + /// + /// The that defines the desired display. + /// A or null, if no device corresponds to the specified index. + public static DisplayDevice GetDisplay(DisplayIndex index) + { + return implementation.GetDisplay(index); + } #endregion diff --git a/Source/OpenTK/DisplayIndex.cs b/Source/OpenTK/DisplayIndex.cs new file mode 100644 index 00000000..14ff6cff --- /dev/null +++ b/Source/OpenTK/DisplayIndex.cs @@ -0,0 +1,72 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK +{ + /// + /// Defines indices. + /// + public enum DisplayIndex + { + /// + /// The first DisplayDevice. + /// + First = 0, + /// + /// The second DisplayDevice. + /// + Second, + /// + /// The third DisplayDevice. + /// + Third, + /// + /// The fourth DisplayDevice. + /// + Fourth, + /// + /// The fifth DisplayDevice. + /// + Fifth, + /// + /// The sixth DisplayDevice. + /// + Sixth, + /// + /// The default (primary) DisplayDevice. + /// + Primary = -1, + /// + /// The default (primary) DisplayDevice. + /// + Default = Primary, + } +} diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index 6daa4102..b44191bc 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -316,7 +316,7 @@ namespace OpenTK /// Not used. protected virtual void OnLoad(EventArgs e) { - if (Load != null) Load(this, e); + Load(this, e); } #endregion @@ -329,7 +329,7 @@ namespace OpenTK /// Not used. protected virtual void OnUnload(EventArgs e) { - if (Unload != null) Unload(this, e); + Unload(this, e); } #endregion @@ -929,22 +929,22 @@ namespace OpenTK /// /// Occurs before the window is displayed for the first time. /// - public event EventHandler Load; + public event EventHandler Load = delegate { }; /// /// Occurs when it is time to render a frame. /// - public event EventHandler RenderFrame; + public event EventHandler RenderFrame = delegate { }; /// /// Occurs before the window is destroyed. /// - public event EventHandler Unload; + public event EventHandler Unload = delegate { }; /// /// Occurs when it is time to update a frame. /// - public event EventHandler UpdateFrame; + public event EventHandler UpdateFrame = delegate { }; #endregion @@ -973,7 +973,7 @@ namespace OpenTK /// protected virtual void OnRenderFrame(FrameEventArgs e) { - if (RenderFrame != null) RenderFrame(this, e); + RenderFrame(this, e); } #endregion @@ -989,7 +989,7 @@ namespace OpenTK /// protected virtual void OnUpdateFrame(FrameEventArgs e) { - if (UpdateFrame != null) UpdateFrame(this, e); + UpdateFrame(this, e); } #endregion @@ -1006,6 +1006,15 @@ namespace OpenTK #region OnResize + /// + /// Called when this window is resized. + /// + /// Not used. + /// + /// You will typically wish to update your viewport whenever + /// the window is resized. See the + /// method. + /// protected override void OnResize(EventArgs e) { base.OnResize(e); diff --git a/Source/OpenTK/Graphics/ColorFormat.cs b/Source/OpenTK/Graphics/ColorFormat.cs index 583109e3..48516503 100644 --- a/Source/OpenTK/Graphics/ColorFormat.cs +++ b/Source/OpenTK/Graphics/ColorFormat.cs @@ -1,9 +1,28 @@ -#region --- License --- -/* Licensed under the MIT/X11 license. - * Copyright (c) 2006-2008 the OpenTK Team. - * This notice may not be removed from any source distribution. - * See license.txt for licensing detailed licensing details. - */ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 using System; @@ -17,13 +36,13 @@ namespace OpenTK.Graphics /// A ColorFormat contains Red, Green, Blue and Alpha components that descibe /// the allocated bits per pixel for the corresponding color. /// - public struct ColorFormat + public struct ColorFormat : IComparable { byte red, green, blue, alpha; bool isIndexed; int bitsPerPixel; - #region --- Constructors --- + #region Constructors /// /// Constructs a new ColorFormat with the specified aggregate bits per pixel. @@ -96,7 +115,7 @@ namespace OpenTK.Graphics #endregion - #region --- Public Methods --- + #region Public Members /// Gets the bits per pixel for the Red channel. public int Red { get { return red; } private set { red = (byte)value; } } @@ -111,9 +130,11 @@ namespace OpenTK.Graphics /// Gets the sum of Red, Green, Blue and Alpha bits per pixel. public int BitsPerPixel { get { return bitsPerPixel; } private set { bitsPerPixel = value; } } + public static readonly ColorFormat Empty = new ColorFormat(0); + #endregion - #region --- Operator Overloads --- + #region Operator Overloads /// /// Converts the specified bpp into a new ColorFormat. @@ -132,7 +153,32 @@ namespace OpenTK.Graphics #endregion - #region --- Overrides --- + #region IComparable Members + + /// + /// Compares two instances. + /// + /// The other instance. + /// + /// Zero if this instance is equal to other; + /// a positive value if this instance is greater than other; + /// a negative value otherwise. + /// + public int CompareTo(ColorFormat other) + { + int result = BitsPerPixel.CompareTo(other.BitsPerPixel); + if (result != 0) + return result; + result = IsIndexed.CompareTo(other.IsIndexed); + if (result != 0) + return result; + result = Alpha.CompareTo(other.Alpha); + return result; + } + + #endregion + + #region Overrides /// /// Indicates whether this instance and a specified object are equal. @@ -176,6 +222,50 @@ namespace OpenTK.Graphics return !(left == right); } + /// + /// Compares two instances for inequality. + /// + /// The left operand. + /// The right operand. + /// True if left is greater than right; false otherwise. + public static bool operator >(ColorFormat left, ColorFormat right) + { + return left.CompareTo(right) > 0; + } + + /// + /// Compares two instances for inequality. + /// + /// The left operand. + /// The right operand. + /// True if left is greater than or equal to right; false otherwise. + public static bool operator >=(ColorFormat left, ColorFormat right) + { + return left.CompareTo(right) >= 0; + } + + /// + /// Compares two instances for inequality. + /// + /// The left operand. + /// The right operand. + /// True if left is less than right; false otherwise. + public static bool operator <(ColorFormat left, ColorFormat right) + { + return left.CompareTo(right) < 0; + } + + /// + /// Compares two instances for inequality. + /// + /// The left operand. + /// The right operand. + /// True if left is less than or equal to right; false otherwise. + public static bool operator <=(ColorFormat left, ColorFormat right) + { + return left.CompareTo(right) <= 0; + } + /// /// Returns the hash code for this instance. /// diff --git a/Source/OpenTK/Graphics/GraphicsContext.cs b/Source/OpenTK/Graphics/GraphicsContext.cs index 54b859d5..38817755 100644 --- a/Source/OpenTK/Graphics/GraphicsContext.cs +++ b/Source/OpenTK/Graphics/GraphicsContext.cs @@ -1,9 +1,28 @@ -#region --- License --- -/* Licensed under the MIT/X11 license. - * Copyright (c) 2006-2008 the OpenTK Team. - * This notice may not be removed from any source distribution. - * See license.txt for licensing detailed licensing details. - */ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 using System; @@ -190,6 +209,39 @@ namespace OpenTK.Graphics #endregion + #region Public Members + + /// + /// Returns a representing this instance. + /// + /// A that contains a string representation of this instance. + public override string ToString() + { + return (this as IGraphicsContextInternal).Context.ToString(); + } + + /// + /// Returns the hash code for this instance. + /// + /// A System.Int32 with the hash code of this instance. + public override int GetHashCode() + { + return (this as IGraphicsContextInternal).Context.GetHashCode(); + } + + /// + /// Compares two instances. + /// + /// The instance to compare to. + /// True, if obj is equal to this instance; false otherwise. + public override bool Equals(object obj) + { + return (obj is GraphicsContext) && + (this as IGraphicsContextInternal).Context == (obj as IGraphicsContextInternal).Context; + } + + #endregion + #region Private Members static IGraphicsContext FindSharedContext() diff --git a/Source/OpenTK/Graphics/GraphicsMode.cs b/Source/OpenTK/Graphics/GraphicsMode.cs index 6854beb0..89379131 100644 --- a/Source/OpenTK/Graphics/GraphicsMode.cs +++ b/Source/OpenTK/Graphics/GraphicsMode.cs @@ -321,8 +321,8 @@ namespace OpenTK.Graphics { if (defaultMode == null) { - Debug.Print("Creating default GraphicsMode ({0}, {1}, {2}, {3}, {4}, {5}, {6}).", DisplayDevice.Default.BitsPerPixel, - 16, 0, 0, 0, 2, false); + Debug.Print("Creating default GraphicsMode ({0}, {1}, {2}, {3}, {4}, {5}, {6}).", + DisplayDevice.Default.BitsPerPixel, 16, 0, 0, 0, 2, false); defaultMode = new GraphicsMode(DisplayDevice.Default.BitsPerPixel, 16, 0, 0, 0, 2, false); } return defaultMode; diff --git a/Source/OpenTK/Graphics/GraphicsModeComparer.cs b/Source/OpenTK/Graphics/GraphicsModeComparer.cs new file mode 100644 index 00000000..ca2e77ff --- /dev/null +++ b/Source/OpenTK/Graphics/GraphicsModeComparer.cs @@ -0,0 +1,63 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK.Graphics +{ + sealed class GraphicsModeComparer : IComparer + { + #region IComparer Members + + public int Compare(GraphicsMode x, GraphicsMode y) + { + int result = x.ColorFormat.CompareTo(y.ColorFormat); + if (result != 0) + return result; + result = x.Depth.CompareTo(y.Depth); + if (result != 0) + return result; + result = x.Stencil.CompareTo(y.Stencil); + if (result != 0) + return result; + result = x.Samples.CompareTo(y.Samples); + if (result != 0) + return result; + result = x.Stereo.CompareTo(y.Stereo); + if (result != 0) + return result; + result = x.Buffers.CompareTo(y.Buffers); + if (result != 0) + return result; + return x.AccumulatorFormat.CompareTo(y.AccumulatorFormat); + } + + #endregion + } +} diff --git a/Source/OpenTK/Graphics/OpenGL/GLHelper.cs b/Source/OpenTK/Graphics/OpenGL/GLHelper.cs index 2e7dd10a..eca8ff49 100644 --- a/Source/OpenTK/Graphics/OpenGL/GLHelper.cs +++ b/Source/OpenTK/Graphics/OpenGL/GLHelper.cs @@ -932,7 +932,7 @@ namespace OpenTK.Graphics.OpenGL public static void TexEnv(TextureEnvTarget target, TextureEnvParameter pname, System.Drawing.Color color) { - Color4 c = new Color4(color); + Color4 c = new Color4(color.R, color.G, color.B, color.A); unsafe { TexEnv(target, pname, &c.R); diff --git a/Source/OpenTK/INativeWindow.cs b/Source/OpenTK/INativeWindow.cs index 6bb3b33b..137a4a8d 100644 --- a/Source/OpenTK/INativeWindow.cs +++ b/Source/OpenTK/INativeWindow.cs @@ -132,6 +132,16 @@ namespace OpenTK [Obsolete] OpenTK.Input.IInputDriver InputDriver { get; } + /// + /// Gets or sets a value, indicating whether the mouse cursor is visible. + /// + bool CursorVisible { get; set; } + +// /// +// /// Gets or sets a value, indicating whether the mouse cursor is confined inside the window size. +// /// +// bool CursorGrabbed { get; set; } + /// /// Closes this window. /// @@ -219,10 +229,20 @@ namespace OpenTK /// event EventHandler WindowStateChanged; + /// + /// Occurs whenever a keybord key is pressed. + /// + event EventHandler KeyDown; + /// /// Occurs whenever a character is typed. /// event EventHandler KeyPress; + + /// + /// Occurs whenever a keyboard key is released. + /// + event EventHandler KeyUp; /// /// Occurs whenever the mouse cursor leaves the window . @@ -241,10 +261,6 @@ namespace OpenTK //event EventHandler MouseClick; //event EventHandler MouseDoubleClick; - //event EventHandler KeyDown; - //event EventHandler KeyUp; - - //event EventHandler DragDrop; //event EventHandler DragEnter; //event EventHandler DragOver; diff --git a/Source/OpenTK/Input/ButtonState.cs b/Source/OpenTK/Input/ButtonState.cs new file mode 100644 index 00000000..597611f8 --- /dev/null +++ b/Source/OpenTK/Input/ButtonState.cs @@ -0,0 +1,46 @@ + #region License + // + // The Open Toolkit Library License + // + // Copyright (c) 2006 - 2010 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 + +namespace OpenTK.Input +{ + /// + /// Enumerates possible mouse button states. + /// + public enum ButtonState + { + /// + /// Indicates that a mouse button is released. + /// + Released = 0, + + /// + /// Indicates that a mouse button is pressed. + /// + Pressed = 1 + } +} + diff --git a/Source/OpenTK/Input/IGamePadDriver.cs b/Source/OpenTK/Input/IGamePadDriver.cs new file mode 100644 index 00000000..0f536dda --- /dev/null +++ b/Source/OpenTK/Input/IGamePadDriver.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK.Input +{ + interface IGamePadDriver + { + } +} diff --git a/Source/OpenTK/Input/IInputDriver2.cs b/Source/OpenTK/Input/IInputDriver2.cs new file mode 100644 index 00000000..1ff89748 --- /dev/null +++ b/Source/OpenTK/Input/IInputDriver2.cs @@ -0,0 +1,41 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK.Input +{ + // Defines the interface for a 2nd generation input driver. + interface IInputDriver2 + { + IMouseDriver2 MouseDriver { get; } + IKeyboardDriver2 KeyboardDriver { get; } + IGamePadDriver GamePadDriver { get; } + } +} diff --git a/Source/OpenTK/Input/IJoystickDriver.cs b/Source/OpenTK/Input/IJoystickDriver.cs index bb2e030f..0b6f73b9 100644 --- a/Source/OpenTK/Input/IJoystickDriver.cs +++ b/Source/OpenTK/Input/IJoystickDriver.cs @@ -34,6 +34,7 @@ namespace OpenTK.Input /// /// Defines the interface for JoystickDevice drivers. /// + [Obsolete] public interface IJoystickDriver { /// diff --git a/Source/OpenTK/Input/IKeyboardDriver.cs b/Source/OpenTK/Input/IKeyboardDriver.cs index e14493f9..20685074 100644 --- a/Source/OpenTK/Input/IKeyboardDriver.cs +++ b/Source/OpenTK/Input/IKeyboardDriver.cs @@ -13,6 +13,7 @@ namespace OpenTK.Input /// /// Defines the interface for KeyboardDevice drivers. /// + [Obsolete] public interface IKeyboardDriver { /// diff --git a/Source/OpenTK/Input/IKeyboardDriver2.cs b/Source/OpenTK/Input/IKeyboardDriver2.cs new file mode 100644 index 00000000..beeed57b --- /dev/null +++ b/Source/OpenTK/Input/IKeyboardDriver2.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK.Input +{ + interface IKeyboardDriver2 + { + /// + /// Retrieves the combined for all keyboard devices. + /// + /// An structure containing the combined state for all keyboard devices. + KeyboardState GetState(); + + /// + /// Retrieves the for the specified keyboard device. + /// + /// The index of the keyboard device. + /// An structure containing the state of the keyboard device. + KeyboardState GetState(int index); + + /// + /// Retrieves the device name for the keyboard device. + /// + /// The index of the keyboard device. + /// A with the name of the specified device or . + /// + /// If no device exists at the specified index, the return value is . + string GetDeviceName(int index); + } +} diff --git a/Source/OpenTK/Input/IMouseDriver.cs b/Source/OpenTK/Input/IMouseDriver.cs index 52abe7af..97713234 100644 --- a/Source/OpenTK/Input/IMouseDriver.cs +++ b/Source/OpenTK/Input/IMouseDriver.cs @@ -13,6 +13,7 @@ namespace OpenTK.Input /// /// Defines the interface for MouseDevice drivers. /// + [Obsolete] public interface IMouseDriver { /// diff --git a/Source/OpenTK/Input/IMouseDriver2.cs b/Source/OpenTK/Input/IMouseDriver2.cs new file mode 100644 index 00000000..0d0335e7 --- /dev/null +++ b/Source/OpenTK/Input/IMouseDriver2.cs @@ -0,0 +1,40 @@ + #region License + // + // The Open Toolkit Library License + // + // Copyright (c) 2006 - 2010 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 + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK.Input +{ + interface IMouseDriver2 + { + MouseState GetState(); + MouseState GetState(int index); + void SetPosition(double x, double y); + } +} diff --git a/Source/OpenTK/Input/InputDriver.cs b/Source/OpenTK/Input/InputDriver.cs deleted file mode 100644 index a1087842..00000000 --- a/Source/OpenTK/Input/InputDriver.cs +++ /dev/null @@ -1,126 +0,0 @@ -#region --- License --- -/* Copyright (c) 2006, 2007 Stefanos Apostolopoulos - * See license.txt for license info - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Text; - -using OpenTK.Input; -using OpenTK.Platform; - -namespace OpenTK -{ - internal class InputDriver : IInputDriver - { - private IInputDriver inputDriver; - - #region --- Constructors --- - - public InputDriver(GameWindow parent) - { - if (parent == null) - throw new ArgumentException("A valid window (IWindowInfo) must be specified to construct an InputDriver"); - - switch (Environment.OSVersion.Platform) - { - case PlatformID.Win32Windows: - case PlatformID.Win32NT: - case PlatformID.Win32S: - case PlatformID.WinCE: - if (Environment.OSVersion.Version.Major > 5 || - (Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor >= 1)) - { - inputDriver = new OpenTK.Platform.Windows.WinRawInput((OpenTK.Platform.Windows.WinWindowInfo)parent.WindowInfo); - } - else - { - // Legacy or unknown windows version: - inputDriver = new OpenTK.Platform.Windows.WMInput((OpenTK.Platform.Windows.WinWindowInfo)parent.WindowInfo); - } - break; - - case PlatformID.Unix: - // TODO: Input is currently handled asychronously by the driver in X11GLNative. - //inputDriver = new OpenTK.Platform.X11.X11Input(parent.WindowInfo); - - break; - - default: - throw new PlatformNotSupportedException( - "Input handling is not supported on the current platform. Please report the problem to http://opentk.sourceforge.net"); - - } - } - - #endregion - - #region --- IInputDriver Members --- - - public void Poll() - { - inputDriver.Poll(); - } - - #endregion - - #region --- IKeyboardDriver Members --- - - public IList Keyboard - { - get { return inputDriver.Keyboard; } - } - - #endregion - - #region --- IMouseDriver Members --- - - public IList Mouse - { - get { return inputDriver.Mouse; } - } - - #endregion - - #region --- IJoystickDriver Members --- - - public IList Joysticks - { - get { return inputDriver.Joysticks; } - } - - #endregion - - #region --- IDisposable Members --- - - private bool disposed; - - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool manual) - { - if (!disposed) - { - if (manual) - { - inputDriver.Dispose(); - } - - disposed = true; - } - } - - ~InputDriver() - { - this.Dispose(false); - } - - #endregion - } -} diff --git a/Source/OpenTK/Input/Keyboard.cs b/Source/OpenTK/Input/Keyboard.cs index 562d63af..09116aa1 100644 --- a/Source/OpenTK/Input/Keyboard.cs +++ b/Source/OpenTK/Input/Keyboard.cs @@ -38,41 +38,64 @@ namespace OpenTK.Input { #region Fields - //static IKeyboardDriver driver; - - #endregion - - #region Constructors - - static Keyboard() - { - throw new NotImplementedException(); - //driver = Platform.Factory.Default.CreateKeyboardDriver(); - } + static readonly IKeyboardDriver2 driver = + Platform.Factory.Default.CreateKeyboardDriver(); + static readonly object SyncRoot = new object(); #endregion #region Public Members /// - /// Retrieves the KeyboardState for the default keyboard device. + /// Retrieves the combined for all keyboard devices. /// - /// A structure containing the state of the keyboard device. + /// An structure containing the combined state for all keyboard devices. public static KeyboardState GetState() { - return new KeyboardState(); + lock (SyncRoot) + { + return driver.GetState(); + } } /// - /// Retrieves the KeyboardState for the specified keyboard device. + /// Retrieves the for the specified keyboard device. /// /// The index of the keyboard device. - /// A structure containing the state of the keyboard device. + /// An structure containing the state of the keyboard device. public static KeyboardState GetState(int index) { - return new KeyboardState(); + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + + lock (SyncRoot) + { + return driver.GetState(index); + } } - #endregion +#if false + // Disabled until a correct, cross-platform API can be defined. + + /// + /// Retrieves the device name for the keyboard device. + /// + /// The index of the keyboard device. + /// A with the name of the specified device or . + /// + /// If no device exists at the specified index, the return value is . + public static string GetDeviceName(int index) + { + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + + lock (SyncRoot) + { + return driver.GetDeviceName(index); + } + } +#endif + + #endregion } } diff --git a/Source/OpenTK/Input/KeyboardState.cs b/Source/OpenTK/Input/KeyboardState.cs index f97c7625..d37c60da 100644 --- a/Source/OpenTK/Input/KeyboardState.cs +++ b/Source/OpenTK/Input/KeyboardState.cs @@ -26,7 +26,7 @@ #endregion using System; -using System.Collections.Generic; +using System.Collections.Specialized; using System.Text; namespace OpenTK.Input @@ -38,26 +38,42 @@ namespace OpenTK.Input { #region Fields - const int NumKeys = ((int)Key.LastKey + 16) / 32; - // Todo: The following line triggers bogus CS0214 in gmcs 2.0.1, sigh... - // Need to add a workaround using either ExplicitLayout or another trick. - //unsafe fixed int Keys[NumKeys]; - - #endregion - - #region Constructors + // Allocate enough ints to store all keyboard keys + const int IntSize = sizeof(int); + const int NumInts = ((int)Key.LastKey + IntSize - 1) / IntSize; + // The following line triggers bogus CS0214 in gmcs 2.0.1, sigh... + unsafe fixed int Keys[NumInts]; + bool is_connected; #endregion #region Public Members + /// + /// Gets a indicating whether the specified + /// is pressed. + /// + /// The to check. + /// True if key is pressed; false otherwise. + public bool this[Key key] + { + get { return IsKeyDown(key); } + internal set + { + if (value) + EnableBit((int)key); + else + DisableBit((int)key); + } + } + /// /// Gets a indicating whether this key is down. /// /// The to check. public bool IsKeyDown(Key key) { - return ReadBit((int)key) != 0; + return ReadBit((int)key); } /// @@ -66,24 +82,178 @@ namespace OpenTK.Input /// The to check. public bool IsKeyUp(Key key) { - return ReadBit((int)key) == 0; + return !ReadBit((int)key); + } + + /// + /// Gets a indicating whether this keyboard + /// is connected. + /// + public bool IsConnected + { + get { return is_connected; } + internal set { is_connected = value; } + } + +#if false + // Disabled until the correct cross-platform API can be determined. + public bool IsLedOn(KeyboardLeds led) + { + return false; + } + + public bool IsLedOff(KeyboardLeds led) + { + return false; + } +#endif + + /// + /// Checks whether two instances are equal. + /// + /// + /// A instance. + /// + /// + /// A instance. + /// + /// + /// True if both left is equal to right; false otherwise. + /// + public static bool operator ==(KeyboardState left, KeyboardState right) + { + return left.Equals(right); + } + + /// + /// Checks whether two instances are not equal. + /// + /// + /// A instance. + /// + /// + /// A instance. + /// + /// + /// True if both left is not equal to right; false otherwise. + /// + public static bool operator !=(KeyboardState left, KeyboardState right) + { + return !left.Equals(right); + } + + /// + /// Compares to an object instance for equality. + /// + /// + /// The to compare to. + /// + /// + /// True if this instance is equal to obj; false otherwise. + /// + public override bool Equals(object obj) + { + if (obj is KeyboardState) + { + return this == (KeyboardState)obj; + } + else + { + return false; + } + } + + /// + /// Generates a hashcode for the current instance. + /// + /// + /// A represting the hashcode for this instance. + /// + public override int GetHashCode() + { + unsafe + { + fixed (int* k = Keys) + { + int hashcode = 0; + for (int i = 0; i < NumInts; i++) + hashcode ^= (k + i)->GetHashCode(); + return hashcode; + } + } } #endregion #region Internal Members - internal int ReadBit(int offset) + internal bool ReadBit(int offset) { - return 0; - //unsafe { return (Keys[(offset / 32)] & (1 << (offset % 32))); } + ValidateOffset(offset); + + int int_offset = offset / 32; + int bit_offset = offset % 32; + unsafe + { + fixed (int* k = Keys) + { + return (*(k + int_offset) & (1 << bit_offset)) != 0u; + } + } } - internal enum BitValue { Zero = 0, One = 1 } - internal void WriteBit(int offset, BitValue bit) + internal void EnableBit(int offset) { - // Todo: this is completely broken. - //unsafe { Keys[offset / 32] = Keys[offset / 32] ^ (~(int)bit << (offset % 32)); } + ValidateOffset(offset); + + int int_offset = offset / 32; + int bit_offset = offset % 32; + unsafe + { + fixed (int* k = Keys) + { + *(k + int_offset) |= 1 << bit_offset; + } + } + } + + internal void DisableBit(int offset) + { + ValidateOffset(offset); + + int int_offset = offset / 32; + int bit_offset = offset % 32; + unsafe + { + fixed (int* k = Keys) + { + *(k + int_offset) &= ~(1 << bit_offset); + } + } + } + + internal void MergeBits(KeyboardState other) + { + unsafe + { + int* k2 = other.Keys; + fixed (int* k1 = Keys) + { + for (int i = 0; i < NumInts; i++) + *(k1 + i) |= *(k2 + i); + } + } + IsConnected |= other.IsConnected; + } + + #endregion + + #region Private Members + + static void ValidateOffset(int offset) + { + if (offset < 0 || offset >= NumInts * IntSize) + throw new ArgumentOutOfRangeException("offset"); } #endregion @@ -97,7 +267,17 @@ namespace OpenTK.Input /// True, if both instances are equal; false otherwise. public bool Equals(KeyboardState other) { - throw new NotImplementedException(); + bool equal = true; + unsafe + { + int* k2 = other.Keys; + fixed (int* k1 = Keys) + { + for (int i = 0; equal && i < NumInts; i++) + equal &= *(k1 + i) == *(k2 + i); + } + } + return equal; } #endregion diff --git a/Source/OpenTK/Input/Mouse.cs b/Source/OpenTK/Input/Mouse.cs index f94aa3c9..f68d5ff2 100644 --- a/Source/OpenTK/Input/Mouse.cs +++ b/Source/OpenTK/Input/Mouse.cs @@ -38,28 +38,57 @@ namespace OpenTK.Input { #region Fields - //static IMouseDriver driver; - - #endregion - - #region Constructors - - static Mouse() - { - } + static readonly IMouseDriver2 driver = + Platform.Factory.Default.CreateMouseDriver(); + static readonly object SyncRoot = new object(); #endregion #region Public Members /// - /// Retrieves the MouseState for the specified mouse device. + /// Retrieves the combined for all specified mouse devices. + /// + /// A structure containing the combined state of all mouse devices. + public static MouseState GetState() + { + lock (SyncRoot) + { + return driver.GetState(); + } + } + + /// + /// Retrieves the for the specified mouse device. /// /// The index of the mouse device. - /// A structure containing the state of the mouse device. + /// A structure containing the state for the specified mouse device. public static MouseState GetState(int index) { - throw new NotImplementedException(); + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + + lock (SyncRoot) + { + return driver.GetState(index); + } + } + + /// + ///Moves the mouse cursor to the specified screen position. + /// + /// + /// A that represents the absolute x position of the cursor in screen coordinates. + /// + /// + /// A that represents the absolute y position of the cursor in screen coordinates. + /// + public static void SetPosition(double x, double y) + { + lock (SyncRoot) + { + driver.SetPosition(x, y); + } } #endregion diff --git a/Source/OpenTK/Input/MouseState.cs b/Source/OpenTK/Input/MouseState.cs index feab5a18..44acc9c3 100644 --- a/Source/OpenTK/Input/MouseState.cs +++ b/Source/OpenTK/Input/MouseState.cs @@ -36,10 +36,303 @@ namespace OpenTK.Input /// public struct MouseState : IEquatable { - #region Constructors + #region Fields - internal MouseState(MouseButton[] buttons) + // Allocate enough ints to store all mouse buttons + const int IntSize = sizeof(int); + const int NumInts = ((int)MouseButton.LastButton + IntSize - 1) / IntSize; + // The following line triggers bogus CS0214 in gmcs 2.0.1, sigh... + unsafe fixed int Buttons[NumInts]; + int x, y; + float wheel; + bool is_connected; + + #endregion + + #region Public Members + + /// + /// Gets a indicating whether the specified + /// is pressed. + /// + /// The to check. + /// True if key is pressed; false otherwise. + public bool this[MouseButton button] { + get { return IsButtonDown(button); } + internal set + { + if (value) + EnableBit((int)button); + else + DisableBit((int)button); + } + } + + /// + /// Gets a indicating whether this button is down. + /// + /// The to check. + public bool IsButtonDown(MouseButton button) + { + return ReadBit((int)button); + } + + /// + /// Gets a indicating whether this button is up. + /// + /// The to check. + public bool IsButtonUp(MouseButton button) + { + return !ReadBit((int)button); + } + + /// + /// Gets the absolute wheel position in integer units. + /// To support high-precision mice, it is recommended to use instead. + /// + public int Wheel + { + get { return (int)Math.Round(wheel, MidpointRounding.AwayFromZero); } + } + + /// + /// Gets the absolute wheel position in floating-point units. + /// + public float WheelPrecise + { + get { return wheel; } + internal set + { + wheel = value; + } + } + + /// + /// Gets an integer representing the absolute x position of the pointer, in window pixel coordinates. + /// + public int X + { + get { return x; } + internal set { x = value; } + } + + /// + /// Gets an integer representing the absolute y position of the pointer, in window pixel coordinates. + /// + public int Y + { + get { return y; } + internal set { y = value; } + } + + /// + /// Gets a indicating whether the left mouse button is pressed. + /// This property is intended for XNA compatibility. + /// + public ButtonState LeftButton + { + get { return IsButtonDown(MouseButton.Left) ? ButtonState.Pressed : ButtonState.Released; } + } + + /// + /// Gets a indicating whether the middle mouse button is pressed. + /// This property is intended for XNA compatibility. + /// + public ButtonState MiddleButton + { + get { return IsButtonDown(MouseButton.Middle) ? ButtonState.Pressed : ButtonState.Released; } + } + + /// + /// Gets a indicating whether the right mouse button is pressed. + /// This property is intended for XNA compatibility. + /// + public ButtonState RightButton + { + get { return IsButtonDown(MouseButton.Right) ? ButtonState.Pressed : ButtonState.Released; } + } + + /// + /// Gets a indicating whether the first extra mouse button is pressed. + /// This property is intended for XNA compatibility. + /// + public ButtonState XButton1 + { + get { return IsButtonDown(MouseButton.Button1) ? ButtonState.Pressed : ButtonState.Released; } + } + + /// + /// Gets a indicating whether the second extra mouse button is pressed. + /// This property is intended for XNA compatibility. + /// + public ButtonState XButton2 + { + get { return IsButtonDown(MouseButton.Button2) ? ButtonState.Pressed : ButtonState.Released; } + } + + /// + /// Gets the absolute wheel position in integer units. This property is intended for XNA compatibility. + /// To support high-precision mice, it is recommended to use instead. + /// + public int ScrollWheelValue + { + get { return Wheel; } + } + + public bool IsConnected + { + get { return is_connected; } + internal set { is_connected = value; } + } + + /// + /// Checks whether two instances are equal. + /// + /// + /// A instance. + /// + /// + /// A instance. + /// + /// + /// True if both left is equal to right; false otherwise. + /// + public static bool operator ==(MouseState left, MouseState right) + { + return left.Equals(right); + } + + /// + /// Checks whether two instances are not equal. + /// + /// + /// A instance. + /// + /// + /// A instance. + /// + /// + /// True if both left is not equal to right; false otherwise. + /// + public static bool operator !=(MouseState left, MouseState right) + { + return !left.Equals(right); + } + + /// + /// Compares to an object instance for equality. + /// + /// + /// The to compare to. + /// + /// + /// True if this instance is equal to obj; false otherwise. + /// + public override bool Equals(object obj) + { + if (obj is MouseState) + { + return this == (MouseState)obj; + } + else + { + return false; + } + } + + /// + /// Generates a hashcode for the current instance. + /// + /// + /// A represting the hashcode for this instance. + /// + public override int GetHashCode() + { + unsafe + { + fixed (int* b = Buttons) + { + return b->GetHashCode() ^ X.GetHashCode() ^ Y.GetHashCode() ^ WheelPrecise.GetHashCode(); + } + } + } + + #endregion + + #region Internal Members + + internal bool ReadBit(int offset) + { + ValidateOffset(offset); + + int int_offset = offset / 32; + int bit_offset = offset % 32; + unsafe + { + fixed (int* b = Buttons) + { + return (*(b + int_offset) & (1 << bit_offset)) != 0u; + } + } + } + + internal void EnableBit(int offset) + { + ValidateOffset(offset); + + int int_offset = offset / 32; + int bit_offset = offset % 32; + unsafe + { + fixed (int* b = Buttons) + { + *(b + int_offset) |= 1 << bit_offset; + } + } + } + + internal void DisableBit(int offset) + { + ValidateOffset(offset); + + int int_offset = offset / 32; + int bit_offset = offset % 32; + unsafe + { + fixed (int* b = Buttons) + { + *(b + int_offset) &= ~(1 << bit_offset); + } + } + } + + internal void MergeBits(MouseState other) + { + unsafe + { + int* b2 = other.Buttons; + fixed (int* b1 = Buttons) + { + for (int i = 0; i < NumInts; i++) + *(b1 + i) |= *(b2 + i); + } + + WheelPrecise += other.WheelPrecise; + X += other.X; + Y += other.Y; + IsConnected |= other.IsConnected; + } + } + + #endregion + + #region Private Members + + static void ValidateOffset(int offset) + { + if (offset < 0 || offset >= NumInts * IntSize) + throw new ArgumentOutOfRangeException("offset"); } #endregion @@ -47,13 +340,24 @@ namespace OpenTK.Input #region IEquatable Members /// - /// Compares two MouseState instances for equality. + /// Compares two MouseState instances. /// - /// The instance to compare to. + /// The instance to compare two. /// True, if both instances are equal; false otherwise. public bool Equals(MouseState other) { - throw new NotImplementedException(); + bool equal = true; + unsafe + { + int* b2 = other.Buttons; + fixed (int* b1 = Buttons) + { + for (int i = 0; equal && i < NumInts; i++) + equal &= *(b1 + i) == *(b2 + i); + } + equal &= X == other.X && Y == other.Y && WheelPrecise == other.WheelPrecise; + } + return equal; } #endregion diff --git a/Source/OpenTK/IntPtrEqualityComparer.cs b/Source/OpenTK/IntPtrEqualityComparer.cs new file mode 100755 index 00000000..6ddb2472 --- /dev/null +++ b/Source/OpenTK/IntPtrEqualityComparer.cs @@ -0,0 +1,49 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 + +using System; +using System.Collections.Generic; + +namespace OpenTK +{ + // Simple equality comparer to allow IntPtrs as keys in dictionaries + // without causing boxing/garbage generation. + // Seriously, Microsoft, shouldn't this have been in the BCL out of the box? + class IntPtrEqualityComparer : IEqualityComparer + { + public bool Equals(IntPtr x, IntPtr y) + { + return x == y; + } + + public int GetHashCode(IntPtr obj) + { + return obj.GetHashCode(); + } + } +} + diff --git a/Source/OpenTK/Math/Box2.cs b/Source/OpenTK/Math/Box2.cs index cbc22b4c..4c54b622 100644 --- a/Source/OpenTK/Math/Box2.cs +++ b/Source/OpenTK/Math/Box2.cs @@ -45,8 +45,8 @@ namespace OpenTK { Left = topLeft.X; Top = topLeft.Y; - Right = topLeft.X; - Bottom = topLeft.Y; + Right = bottomRight.X; + Bottom = bottomRight.Y; } /// diff --git a/Source/OpenTK/Math/Matrix4.cs b/Source/OpenTK/Math/Matrix4.cs index 8eb5a484..009b658f 100644 --- a/Source/OpenTK/Math/Matrix4.cs +++ b/Source/OpenTK/Math/Matrix4.cs @@ -565,8 +565,6 @@ namespace OpenTK throw new ArgumentOutOfRangeException("zNear"); if (zFar <= 0) throw new ArgumentOutOfRangeException("zFar"); - if (zNear >= zFar) - throw new ArgumentOutOfRangeException("zNear"); float yMax = zNear * (float)System.Math.Tan(0.5f * fovy); float yMin = -yMax; diff --git a/Source/OpenTK/Math/Matrix4d.cs b/Source/OpenTK/Math/Matrix4d.cs index 95a7408f..1a1f810b 100644 --- a/Source/OpenTK/Math/Matrix4d.cs +++ b/Source/OpenTK/Math/Matrix4d.cs @@ -565,8 +565,6 @@ namespace OpenTK throw new ArgumentOutOfRangeException("zNear"); if (zFar <= 0) throw new ArgumentOutOfRangeException("zFar"); - if (zNear >= zFar) - throw new ArgumentOutOfRangeException("zNear"); double yMax = zNear * System.Math.Tan(0.5 * fovy); double yMin = -yMax; diff --git a/Source/OpenTK/Math/Vector2.cs b/Source/OpenTK/Math/Vector2.cs index 5946fdd3..7c8dabf8 100644 --- a/Source/OpenTK/Math/Vector2.cs +++ b/Source/OpenTK/Math/Vector2.cs @@ -50,6 +50,16 @@ namespace OpenTK #region Constructors + /// + /// Constructs a new instance. + /// + /// The value that will initialize this instance. + public Vector2(float value) + { + X = value; + Y = value; + } + /// /// Constructs a new Vector2. /// diff --git a/Source/OpenTK/Math/Vector2d.cs b/Source/OpenTK/Math/Vector2d.cs index e0aae38e..5305a185 100644 --- a/Source/OpenTK/Math/Vector2d.cs +++ b/Source/OpenTK/Math/Vector2d.cs @@ -69,6 +69,16 @@ namespace OpenTK #region Constructors + /// + /// Constructs a new instance. + /// + /// The value that will initialize this instance. + public Vector2d(double value) + { + X = value; + Y = value; + } + /// Constructs left vector with the given coordinates. /// The X coordinate. /// The Y coordinate. diff --git a/Source/OpenTK/Math/Vector2h.cs b/Source/OpenTK/Math/Vector2h.cs index 28bc8e40..0ac6d509 100644 --- a/Source/OpenTK/Math/Vector2h.cs +++ b/Source/OpenTK/Math/Vector2h.cs @@ -46,6 +46,26 @@ namespace OpenTK #region Constructors + /// + /// Constructs a new instance. + /// + /// The value that will initialize this instance. + public Vector2h(Half value) + { + X = value; + Y = value; + } + + /// + /// Constructs a new instance. + /// + /// The value that will initialize this instance. + public Vector2h(Single value) + { + X = new Half(value); + Y = new Half(value); + } + /// /// The new Half2 instance will avoid conversion and copy directly from the Half parameters. /// diff --git a/Source/OpenTK/Math/Vector3.cs b/Source/OpenTK/Math/Vector3.cs index 9baa652e..5ce13d85 100644 --- a/Source/OpenTK/Math/Vector3.cs +++ b/Source/OpenTK/Math/Vector3.cs @@ -58,6 +58,17 @@ namespace OpenTK #region Constructors + /// + /// Constructs a new instance. + /// + /// The value that will initialize this instance. + public Vector3(float value) + { + X = value; + Y = value; + Z = value; + } + /// /// Constructs a new Vector3. /// @@ -1143,7 +1154,7 @@ namespace OpenTK /// The transformed vector public static void TransformPerspective(ref Vector3 vec, ref Matrix4 mat, out Vector3 result) { - Vector4 v = new Vector4(vec); + Vector4 v = new Vector4(vec, 1); Vector4.Transform(ref v, ref mat, out v); result.X = v.X / v.W; result.Y = v.Y / v.W; diff --git a/Source/OpenTK/Math/Vector3d.cs b/Source/OpenTK/Math/Vector3d.cs index bf2d60b9..5af1aeb5 100644 --- a/Source/OpenTK/Math/Vector3d.cs +++ b/Source/OpenTK/Math/Vector3d.cs @@ -56,6 +56,17 @@ namespace OpenTK #region Constructors + /// + /// Constructs a new instance. + /// + /// The value that will initialize this instance. + public Vector3d(double value) + { + X = value; + Y = value; + Z = value; + } + /// /// Constructs a new Vector3. /// @@ -1141,7 +1152,7 @@ namespace OpenTK /// The transformed vector public static void TransformPerspective(ref Vector3d vec, ref Matrix4d mat, out Vector3d result) { - Vector4d v = new Vector4d(vec); + Vector4d v = new Vector4d(vec, 1); Vector4d.Transform(ref v, ref mat, out v); result.X = v.X / v.W; result.Y = v.Y / v.W; @@ -1353,10 +1364,10 @@ namespace OpenTK /// True if the instances are equal; false otherwise. public override bool Equals(object obj) { - if (!(obj is Vector3)) + if (!(obj is Vector3d)) return false; - return this.Equals((Vector3)obj); + return this.Equals((Vector3d)obj); } #endregion diff --git a/Source/OpenTK/Math/Vector3h.cs b/Source/OpenTK/Math/Vector3h.cs index 973bbad8..7f417808 100644 --- a/Source/OpenTK/Math/Vector3h.cs +++ b/Source/OpenTK/Math/Vector3h.cs @@ -51,6 +51,28 @@ namespace OpenTK #region Constructors + /// + /// Constructs a new instance. + /// + /// The value that will initialize this instance. + public Vector3h(Half value) + { + X = value; + Y = value; + Z = value; + } + + /// + /// Constructs a new instance. + /// + /// The value that will initialize this instance. + public Vector3h(Single value) + { + X = new Half(value); + Y = new Half(value); + Z = new Half(value); + } + /// /// The new Half3 instance will avoid conversion and copy directly from the Half parameters. /// diff --git a/Source/OpenTK/Math/Vector4.cs b/Source/OpenTK/Math/Vector4.cs index f13a6fe0..384fdf74 100644 --- a/Source/OpenTK/Math/Vector4.cs +++ b/Source/OpenTK/Math/Vector4.cs @@ -96,6 +96,18 @@ namespace OpenTK #region Constructors + /// + /// Constructs a new instance. + /// + /// The value that will initialize this instance. + public Vector4(float value) + { + X = value; + Y = value; + Z = value; + W = value; + } + /// /// Constructs a new Vector4. /// @@ -125,8 +137,10 @@ namespace OpenTK /// /// Constructs a new Vector4 from the given Vector3. + /// The w component is initialized to 0. /// /// The Vector3 to copy components from. + /// public Vector4(Vector3 v) { X = v.X; diff --git a/Source/OpenTK/Math/Vector4d.cs b/Source/OpenTK/Math/Vector4d.cs index 246191d0..95eec840 100644 --- a/Source/OpenTK/Math/Vector4d.cs +++ b/Source/OpenTK/Math/Vector4d.cs @@ -94,6 +94,18 @@ namespace OpenTK #region Constructors + /// + /// Constructs a new instance. + /// + /// The value that will initialize this instance. + public Vector4d(double value) + { + X = value; + Y = value; + Z = value; + W = value; + } + /// /// Constructs a new Vector4d. /// @@ -123,8 +135,10 @@ namespace OpenTK /// /// Constructs a new Vector4d from the given Vector3d. + /// The w component is initialized to 0. /// /// The Vector3d to copy components from. + /// public Vector4d(Vector3d v) { X = v.X; diff --git a/Source/OpenTK/Math/Vector4h.cs b/Source/OpenTK/Math/Vector4h.cs index 744c1436..4a7f83a6 100644 --- a/Source/OpenTK/Math/Vector4h.cs +++ b/Source/OpenTK/Math/Vector4h.cs @@ -54,6 +54,30 @@ namespace OpenTK #region Constructors + /// + /// Constructs a new instance. + /// + /// The value that will initialize this instance. + public Vector4h(Half value) + { + X = value; + Y = value; + Z = value; + W = value; + } + + /// + /// Constructs a new instance. + /// + /// The value that will initialize this instance. + public Vector4h(Single value) + { + X = new Half(value); + Y = new Half(value); + Z = new Half(value); + W = new Half(value); + } + /// /// The new Half4 instance will avoid conversion and copy directly from the Half parameters. /// diff --git a/Source/OpenTK/NativeWindow.cs b/Source/OpenTK/NativeWindow.cs index f6f00857..5fc2f418 100644 --- a/Source/OpenTK/NativeWindow.cs +++ b/Source/OpenTK/NativeWindow.cs @@ -49,6 +49,8 @@ namespace OpenTK private readonly INativeWindow implementation; private bool disposed, events; + private bool cursor_visible = true; + private bool previous_cursor_visible = true; #endregion @@ -541,6 +543,23 @@ namespace OpenTK #endregion + #region CursorVisible + + /// + /// Gets or sets a value indicating whether the mouse cursor is visible. + /// + public bool CursorVisible + { + get { return cursor_visible; } + set + { + cursor_visible = value; + implementation.CursorVisible = value; + } + } + + #endregion + #endregion #region Events @@ -548,72 +567,82 @@ namespace OpenTK /// /// Occurs after the window has closed. /// - public event EventHandler Closed; + public event EventHandler Closed = delegate { }; /// /// Occurs when the window is about to close. /// - public event EventHandler Closing; + public event EventHandler Closing = delegate { }; /// /// Occurs when the window is disposed. /// - public event EventHandler Disposed; + public event EventHandler Disposed = delegate { }; /// /// Occurs when the property of the window changes. /// - public event EventHandler FocusedChanged; + public event EventHandler FocusedChanged = delegate { }; /// /// Occurs when the property of the window changes. /// - public event EventHandler IconChanged; + public event EventHandler IconChanged = delegate { }; + + /// + /// Occurs whenever a keybord key is pressed. + /// + public event EventHandler KeyDown = delegate { }; /// /// Occurs whenever a character is typed. /// - public event EventHandler KeyPress; + public event EventHandler KeyPress = delegate { }; + + /// + /// Occurs whenever a keyboard key is released. + /// + public event EventHandler KeyUp = delegate { }; /// /// Occurs whenever the window is moved. /// - public event EventHandler Move; + public event EventHandler Move = delegate { }; /// /// Occurs whenever the mouse cursor enters the window . /// - public event EventHandler MouseEnter; + public event EventHandler MouseEnter = delegate { }; /// /// Occurs whenever the mouse cursor leaves the window . /// - public event EventHandler MouseLeave; + public event EventHandler MouseLeave = delegate { }; /// /// Occurs whenever the window is resized. /// - public event EventHandler Resize; + public event EventHandler Resize = delegate { }; /// /// Occurs when the property of the window changes. /// - public event EventHandler TitleChanged; + public event EventHandler TitleChanged = delegate { }; /// /// Occurs when the property of the window changes. /// - public event EventHandler VisibleChanged; + public event EventHandler VisibleChanged = delegate { }; /// /// Occurs when the property of the window changes. /// - public event EventHandler WindowBorderChanged; + public event EventHandler WindowBorderChanged = delegate { }; /// /// Occurs when the property of the window changes. /// - public event EventHandler WindowStateChanged; + public event EventHandler WindowStateChanged = delegate { }; #endregion @@ -687,7 +716,7 @@ namespace OpenTK /// Not used. protected virtual void OnClosed(EventArgs e) { - if (Closed != null) Closed(this, e); + Closed(this, e); } #endregion @@ -702,7 +731,7 @@ namespace OpenTK /// Set e.Cancel to true in order to stop the NativeWindow from closing. protected virtual void OnClosing(CancelEventArgs e) { - if (Closing != null) Closing(this, e); + Closing(this, e); } #endregion @@ -715,7 +744,7 @@ namespace OpenTK /// Not used. protected virtual void OnDisposed(EventArgs e) { - if (Disposed != null) Disposed(this, e); + Disposed(this, e); } #endregion @@ -728,7 +757,21 @@ namespace OpenTK /// Not used. protected virtual void OnFocusedChanged(EventArgs e) { - if (FocusedChanged != null) FocusedChanged(this, e); + if (!Focused) + { + // Release cursor when losing focus, to ensure + // IDEs continue working as expected. + previous_cursor_visible = CursorVisible; + CursorVisible = true; + } + else if (!previous_cursor_visible) + { + // Make cursor invisible when focus is regained + // if cursor was invisible on previous focus loss. + previous_cursor_visible = true; + CursorVisible = false; + } + FocusedChanged(this, e); } #endregion @@ -741,7 +784,19 @@ namespace OpenTK /// Not used. protected virtual void OnIconChanged(EventArgs e) { - if (IconChanged != null) IconChanged(this, e); + IconChanged(this, e); + } + + #endregion + + #region OnKeyDown + + /// + /// Occurs whenever a keybord key is pressed. + /// + protected virtual void OnKeyDown(KeyboardKeyEventArgs e) + { + KeyDown(this, e); } #endregion @@ -754,7 +809,20 @@ namespace OpenTK /// The for this event. protected virtual void OnKeyPress(KeyPressEventArgs e) { - if (KeyPress != null) KeyPress(this, e); + KeyPress(this, e); + } + + #endregion + + #region OnKeyUp + + /// + /// Called when a keybord key is released. + /// + /// The for this event. + protected virtual void OnKeyUp(KeyboardKeyEventArgs e) + { + KeyUp(this, e); } #endregion @@ -767,7 +835,7 @@ namespace OpenTK /// Not used. protected virtual void OnMove(EventArgs e) { - if (Move != null) Move(this, e); + Move(this, e); } #endregion @@ -780,7 +848,7 @@ namespace OpenTK /// Not used. protected virtual void OnMouseEnter(EventArgs e) { - if (MouseEnter != null) MouseEnter(this, e); + MouseEnter(this, e); } #endregion @@ -793,7 +861,7 @@ namespace OpenTK /// Not used. protected virtual void OnMouseLeave(EventArgs e) { - if (MouseLeave != null) MouseLeave(this, e); + MouseLeave(this, e); } #endregion @@ -806,7 +874,7 @@ namespace OpenTK /// Not used. protected virtual void OnResize(EventArgs e) { - if (Resize != null) Resize(this, e); + Resize(this, e); } #endregion @@ -819,7 +887,7 @@ namespace OpenTK /// Not used. protected virtual void OnTitleChanged(EventArgs e) { - if (TitleChanged != null) TitleChanged(this, e); + TitleChanged(this, e); } #endregion @@ -832,7 +900,7 @@ namespace OpenTK /// Not used. protected virtual void OnVisibleChanged(EventArgs e) { - if (VisibleChanged != null) VisibleChanged(this, e); + VisibleChanged(this, e); } #endregion @@ -845,7 +913,7 @@ namespace OpenTK /// Not used. protected virtual void OnWindowBorderChanged(EventArgs e) { - if (WindowBorderChanged != null) WindowBorderChanged(this, e); + WindowBorderChanged(this, e); } #endregion @@ -858,7 +926,7 @@ namespace OpenTK /// Not used. protected virtual void OnWindowStateChanged(EventArgs e) { - if (WindowStateChanged != null) WindowStateChanged(this, e); + WindowStateChanged(this, e); } #endregion diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index dc177639..9cd3f603 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -1,4 +1,4 @@ - + Local @@ -57,6 +57,7 @@ 4 AllRules.ruleset full + true true @@ -76,6 +77,11 @@ ..\..\Binaries\OpenTK\Release\ + none + 4 + true + TRACE; + true true @@ -112,10 +118,6 @@ System.Drawing False - - System.Windows.Forms - False - System.Xml False @@ -128,6 +130,14 @@ Code + + + + + + + + Code @@ -548,9 +558,6 @@ Code - - Code - Code @@ -753,9 +760,15 @@ OpenTK.snk - + + + + + Always - + + + diff --git a/Source/OpenTK/Platform/DisplayDeviceBase.cs b/Source/OpenTK/Platform/DisplayDeviceBase.cs new file mode 100644 index 00000000..723d8221 --- /dev/null +++ b/Source/OpenTK/Platform/DisplayDeviceBase.cs @@ -0,0 +1,53 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK.Platform +{ + abstract class DisplayDeviceBase : IDisplayDeviceDriver + { + protected readonly List AvailableDevices = new List(); + protected DisplayDevice Primary; + + public abstract bool TryChangeResolution(DisplayDevice device, DisplayResolution resolution); + public abstract bool TryRestoreResolution(DisplayDevice device); + + // Gets the DisplayDevice that corresponds to the specified index. + public virtual DisplayDevice GetDisplay(DisplayIndex index) + { + if (index == DisplayIndex.Primary) + return Primary; + else if ((int)index >= 0 && (int)index < AvailableDevices.Count) + return AvailableDevices[(int)index]; + else + return null; + } + } +} diff --git a/Source/OpenTK/Platform/Factory.cs b/Source/OpenTK/Platform/Factory.cs index 3ac7c3da..cb9ab3d9 100644 --- a/Source/OpenTK/Platform/Factory.cs +++ b/Source/OpenTK/Platform/Factory.cs @@ -114,11 +114,16 @@ namespace OpenTK.Platform return default_implementation.CreateGraphicsMode(); } - public OpenTK.Input.IKeyboardDriver CreateKeyboardDriver() + public OpenTK.Input.IKeyboardDriver2 CreateKeyboardDriver() { return default_implementation.CreateKeyboardDriver(); } + public OpenTK.Input.IMouseDriver2 CreateMouseDriver() + { + return default_implementation.CreateMouseDriver(); + } + class UnsupportedPlatform : IPlatformFactory { #region Fields @@ -164,7 +169,12 @@ namespace OpenTK.Platform throw new PlatformNotSupportedException(error_string); } - public OpenTK.Input.IKeyboardDriver CreateKeyboardDriver() + public OpenTK.Input.IKeyboardDriver2 CreateKeyboardDriver() + { + throw new PlatformNotSupportedException(error_string); + } + + public OpenTK.Input.IMouseDriver2 CreateMouseDriver() { throw new PlatformNotSupportedException(error_string); } diff --git a/Source/OpenTK/Platform/IDisplayDeviceDriver.cs b/Source/OpenTK/Platform/IDisplayDeviceDriver.cs index f66bca4e..cc3e67da 100644 --- a/Source/OpenTK/Platform/IDisplayDeviceDriver.cs +++ b/Source/OpenTK/Platform/IDisplayDeviceDriver.cs @@ -1,9 +1,28 @@ -#region --- License --- -/* Licensed under the MIT/X11 license. - * Copyright (c) 2006-2008 the OpenTK team. - * This notice may not be removed. - * See license.txt for licensing detailed licensing details. - */ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 using System; @@ -16,5 +35,6 @@ namespace OpenTK.Platform { bool TryChangeResolution(DisplayDevice device, DisplayResolution resolution); bool TryRestoreResolution(DisplayDevice device); + DisplayDevice GetDisplay(DisplayIndex displayIndex); } } diff --git a/Source/OpenTK/Platform/IPlatformFactory.cs b/Source/OpenTK/Platform/IPlatformFactory.cs index 8bd81b5a..94761623 100644 --- a/Source/OpenTK/Platform/IPlatformFactory.cs +++ b/Source/OpenTK/Platform/IPlatformFactory.cs @@ -47,6 +47,8 @@ namespace OpenTK.Platform IGraphicsMode CreateGraphicsMode(); - OpenTK.Input.IKeyboardDriver CreateKeyboardDriver(); + OpenTK.Input.IKeyboardDriver2 CreateKeyboardDriver(); + + OpenTK.Input.IMouseDriver2 CreateMouseDriver(); } } diff --git a/Source/OpenTK/Platform/MacOS/AglContext.cs b/Source/OpenTK/Platform/MacOS/AglContext.cs index 50ab9144..4d2ca42e 100644 --- a/Source/OpenTK/Platform/MacOS/AglContext.cs +++ b/Source/OpenTK/Platform/MacOS/AglContext.cs @@ -1,17 +1,36 @@ +#region License // -// -// AglContext.cs +// The Open Toolkit Library License // -// Created by Erik Ylvisaker on 3/17/08. -// Copyright 2008. All rights reserved. +// Copyright (c) 2006 - 2010 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 + +// Created by Erik Ylvisaker on 3/17/08. using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; -using Control = System.Windows.Forms.Control; namespace OpenTK.Platform.MacOS { @@ -23,41 +42,40 @@ namespace OpenTK.Platform.MacOS using AGLContext = IntPtr; using AGLPbuffer = IntPtr; - class AglContext : DesktopGraphicsContext + class AglContext : DesktopGraphicsContext { bool mVSync = false; // Todo: keep track of which display adapter was specified when the context was created. // IntPtr displayID; - + GraphicsMode graphics_mode; CarbonWindowInfo carbonWindow; IntPtr shareContextRef; - DisplayDevice device; - bool mIsFullscreen = false; + DisplayDevice device; + bool mIsFullscreen = false; public AglContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext) { Debug.Print("Context Type: {0}", shareContext); Debug.Print("Window info: {0}", window); - + this.graphics_mode = mode; this.carbonWindow = (CarbonWindowInfo)window; - + if (shareContext is AglContext) shareContextRef = ((AglContext)shareContext).Handle.Handle; - if (shareContext is GraphicsContext) - { - ContextHandle shareHandle = shareContext != null ? - (shareContext as IGraphicsContextInternal).Context : (ContextHandle)IntPtr.Zero; - - shareContextRef = shareHandle.Handle; - } - - if (shareContextRef == IntPtr.Zero) - { - Debug.Print("No context sharing will take place."); - } - + if (shareContext is GraphicsContext) + { + ContextHandle shareHandle = shareContext != null ? (shareContext as IGraphicsContextInternal).Context : (ContextHandle)IntPtr.Zero; + + shareContextRef = shareHandle.Handle; + } + + if (shareContextRef == IntPtr.Zero) + { + Debug.Print("No context sharing will take place."); + } + CreateContext(mode, carbonWindow, shareContextRef, true); } @@ -67,7 +85,7 @@ namespace OpenTK.Platform.MacOS throw new ArgumentException("handle"); if (window == null) throw new ArgumentNullException("window"); - + Handle = handle; carbonWindow = (CarbonWindowInfo)window; } @@ -76,37 +94,36 @@ namespace OpenTK.Platform.MacOS private void AddPixelAttrib(List aglAttributes, Agl.PixelFormatAttribute pixelFormatAttribute) { Debug.Print(pixelFormatAttribute.ToString()); - + aglAttributes.Add((int)pixelFormatAttribute); } private void AddPixelAttrib(List aglAttributes, Agl.PixelFormatAttribute pixelFormatAttribute, int value) { Debug.Print("{0} : {1}", pixelFormatAttribute, value); - + aglAttributes.Add((int)pixelFormatAttribute); aglAttributes.Add(value); } - void CreateContext(GraphicsMode mode, CarbonWindowInfo carbonWindow, - IntPtr shareContextRef, bool fullscreen) + void CreateContext(GraphicsMode mode, CarbonWindowInfo carbonWindow, IntPtr shareContextRef, bool fullscreen) { - List aglAttributes = new List(); - + List aglAttributes = new List(); + Debug.Print("AGL pixel format attributes:"); Debug.Indent(); - + AddPixelAttrib(aglAttributes, Agl.PixelFormatAttribute.AGL_RGBA); AddPixelAttrib(aglAttributes, Agl.PixelFormatAttribute.AGL_DOUBLEBUFFER); AddPixelAttrib(aglAttributes, Agl.PixelFormatAttribute.AGL_RED_SIZE, mode.ColorFormat.Red); AddPixelAttrib(aglAttributes, Agl.PixelFormatAttribute.AGL_GREEN_SIZE, mode.ColorFormat.Green); AddPixelAttrib(aglAttributes, Agl.PixelFormatAttribute.AGL_BLUE_SIZE, mode.ColorFormat.Blue); AddPixelAttrib(aglAttributes, Agl.PixelFormatAttribute.AGL_ALPHA_SIZE, mode.ColorFormat.Alpha); - + if (mode.Depth > 0) AddPixelAttrib(aglAttributes, Agl.PixelFormatAttribute.AGL_DEPTH_SIZE, mode.Depth); - + if (mode.Stencil > 0) AddPixelAttrib(aglAttributes, Agl.PixelFormatAttribute.AGL_STENCIL_SIZE, mode.Stencil); - + if (mode.AccumulatorFormat.BitsPerPixel > 0) { AddPixelAttrib(aglAttributes, Agl.PixelFormatAttribute.AGL_ACCUM_RED_SIZE, mode.AccumulatorFormat.Red); @@ -120,72 +137,68 @@ namespace OpenTK.Platform.MacOS AddPixelAttrib(aglAttributes, Agl.PixelFormatAttribute.AGL_SAMPLE_BUFFERS_ARB, 1); AddPixelAttrib(aglAttributes, Agl.PixelFormatAttribute.AGL_SAMPLES_ARB, mode.Samples); } - + if (fullscreen) { AddPixelAttrib(aglAttributes, Agl.PixelFormatAttribute.AGL_FULLSCREEN); } AddPixelAttrib(aglAttributes, Agl.PixelFormatAttribute.AGL_NONE); - + Debug.Unindent(); - + Debug.Write("Attribute array: "); for (int i = 0; i < aglAttributes.Count; i++) Debug.Write(aglAttributes[i].ToString() + " "); Debug.WriteLine(""); - + AGLPixelFormat myAGLPixelFormat; - + // Choose a pixel format with the attributes we specified. if (fullscreen) { - IntPtr gdevice; - IntPtr cgdevice = GetQuartzDevice(carbonWindow); - - if (cgdevice == IntPtr.Zero) - cgdevice = QuartzDisplayDeviceDriver.MainDisplay; - - OSStatus status = Carbon.API.DMGetGDeviceByDisplayID( - cgdevice, out gdevice, false); + IntPtr gdevice; + IntPtr cgdevice = GetQuartzDevice(carbonWindow); + + if (cgdevice == IntPtr.Zero) + cgdevice = (IntPtr)DisplayDevice.Default.Id; + + OSStatus status = Carbon.API.DMGetGDeviceByDisplayID(cgdevice, out gdevice, false); if (status != OSStatus.NoError) throw new MacOSException(status, "DMGetGDeviceByDisplayID failed."); - - myAGLPixelFormat = Agl.aglChoosePixelFormat( - ref gdevice, 1, - aglAttributes.ToArray()); - + + myAGLPixelFormat = Agl.aglChoosePixelFormat(ref gdevice, 1, aglAttributes.ToArray()); + Agl.AglError err = Agl.GetError(); - + if (err == Agl.AglError.BadPixelFormat) { Debug.Print("Failed to create full screen pixel format."); Debug.Print("Trying again to create a non-fullscreen pixel format."); - + CreateContext(mode, carbonWindow, shareContextRef, false); return; } } + else { - myAGLPixelFormat = Agl.aglChoosePixelFormat( - IntPtr.Zero, 0, - aglAttributes.ToArray()); - + myAGLPixelFormat = Agl.aglChoosePixelFormat(IntPtr.Zero, 0, aglAttributes.ToArray()); + MyAGLReportError("aglChoosePixelFormat"); } - - - Debug.Print("Creating AGL context. Sharing with {0}", shareContextRef); - + + + Debug.Print("Creating AGL context. Sharing with {0}", shareContextRef); + // create the context and share it with the share reference. - Handle = new ContextHandle( Agl.aglCreateContext(myAGLPixelFormat, shareContextRef)); + Handle = new ContextHandle(Agl.aglCreateContext(myAGLPixelFormat, shareContextRef)); MyAGLReportError("aglCreateContext"); - + // Free the pixel format from memory. Agl.aglDestroyPixelFormat(myAGLPixelFormat); MyAGLReportError("aglDestroyPixelFormat"); - + Debug.Print("IsControl: {0}", carbonWindow.IsControl); SetDrawable(carbonWindow); @@ -193,46 +206,48 @@ namespace OpenTK.Platform.MacOS Update(carbonWindow); MakeCurrent(carbonWindow); - + Debug.Print("context: {0}", Handle.Handle); } - private IntPtr GetQuartzDevice(CarbonWindowInfo carbonWindow) - { - IntPtr windowRef = carbonWindow.WindowRef; - - if (CarbonGLNative.WindowRefMap.ContainsKey(windowRef) == false) - return IntPtr.Zero; - - WeakReference nativeRef = CarbonGLNative.WindowRefMap[windowRef]; - if (nativeRef.IsAlive == false) - return IntPtr.Zero; - - CarbonGLNative window = nativeRef.Target as CarbonGLNative; - - if (window == null) - return IntPtr.Zero; - - return QuartzDisplayDeviceDriver.HandleTo(window.TargetDisplayDevice); - - } + private IntPtr GetQuartzDevice(CarbonWindowInfo carbonWindow) + { + IntPtr windowRef = carbonWindow.WindowRef; + + if (CarbonGLNative.WindowRefMap.ContainsKey(windowRef) == false) + return IntPtr.Zero; + + WeakReference nativeRef = CarbonGLNative.WindowRefMap[windowRef]; + if (nativeRef.IsAlive == false) + return IntPtr.Zero; + + CarbonGLNative window = nativeRef.Target as CarbonGLNative; + + if (window == null) + return IntPtr.Zero; + + return QuartzDisplayDeviceDriver.HandleTo(window.TargetDisplayDevice); + + } void SetBufferRect(CarbonWindowInfo carbonWindow) { if (carbonWindow.IsControl == false) return; - + + // Todo: See if there is a way around using WinForms. + throw new NotImplementedException(); +#if false System.Windows.Forms.Control ctrl = Control.FromHandle(carbonWindow.WindowRef); - + if (ctrl.TopLevelControl == null) return; - + Rect rect = API.GetControlBounds(carbonWindow.WindowRef); - System.Windows.Forms.Form frm = (System.Windows.Forms.Form) ctrl.TopLevelControl; - - System.Drawing.Point loc = - frm.PointToClient(ctrl.PointToScreen(System.Drawing.Point.Empty)); - + System.Windows.Forms.Form frm = (System.Windows.Forms.Form)ctrl.TopLevelControl; + + System.Drawing.Point loc = frm.PointToClient(ctrl.PointToScreen(System.Drawing.Point.Empty)); + rect.X = (short)loc.X; rect.Y = (short)loc.Y; @@ -243,28 +258,28 @@ namespace OpenTK.Platform.MacOS Debug.Print(" AGL Coordinate Rect: {0}", rect); int[] glrect = new int[4]; - + glrect[0] = rect.X; glrect[1] = rect.Y; glrect[2] = rect.Width; glrect[3] = rect.Height; - + Agl.aglSetInteger(Handle.Handle, Agl.ParameterNames.AGL_BUFFER_RECT, glrect); MyAGLReportError("aglSetInteger"); - + Agl.aglEnable(Handle.Handle, Agl.ParameterNames.AGL_BUFFER_RECT); MyAGLReportError("aglEnable"); - +#endif } void SetDrawable(CarbonWindowInfo carbonWindow) { IntPtr windowPort = GetWindowPortForWindowInfo(carbonWindow); - //Debug.Print("Setting drawable for context {0} to window port: {1}", Handle.Handle, windowPort); - + //Debug.Print("Setting drawable for context {0} to window port: {1}", Handle.Handle, windowPort); + Agl.aglSetDrawable(Handle.Handle, windowPort); - + MyAGLReportError("aglSetDrawable"); - + } private static IntPtr GetWindowPortForWindowInfo(CarbonWindowInfo carbonWindow) @@ -276,109 +291,109 @@ namespace OpenTK.Platform.MacOS windowPort = API.GetWindowPort(controlOwner); } + else windowPort = API.GetWindowPort(carbonWindow.WindowRef); - + return windowPort; } - public override void Update(IWindowInfo window) + public override void Update(IWindowInfo window) { CarbonWindowInfo carbonWindow = (CarbonWindowInfo)window; + + if (carbonWindow.GoFullScreenHack) + { + carbonWindow.GoFullScreenHack = false; + CarbonGLNative wind = GetCarbonWindow(carbonWindow); + + if (wind != null) + wind.SetFullscreen(this); + else + Debug.Print("Could not find window!"); + + return; + } - if (carbonWindow.GoFullScreenHack) - { - carbonWindow.GoFullScreenHack = false; - CarbonGLNative wind = GetCarbonWindow(carbonWindow); - - if (wind != null) - wind.SetFullscreen(this); - else - Debug.Print("Could not find window!"); - - return; - } - else if (carbonWindow.GoWindowedHack) - { - carbonWindow.GoWindowedHack = false; - CarbonGLNative wind = GetCarbonWindow(carbonWindow); - - if (wind != null) - wind.UnsetFullscreen(this); - else - Debug.Print("Could not find window!"); - - } - - if (mIsFullscreen) - return; - + else if (carbonWindow.GoWindowedHack) + { + carbonWindow.GoWindowedHack = false; + CarbonGLNative wind = GetCarbonWindow(carbonWindow); + + if (wind != null) + wind.UnsetFullscreen(this); + else + Debug.Print("Could not find window!"); + + } + + if (mIsFullscreen) + return; + SetDrawable(carbonWindow); SetBufferRect(carbonWindow); - + Agl.aglUpdateContext(Handle.Handle); } - private CarbonGLNative GetCarbonWindow(CarbonWindowInfo carbonWindow) - { - WeakReference r = CarbonGLNative.WindowRefMap[carbonWindow.WindowRef]; + private CarbonGLNative GetCarbonWindow(CarbonWindowInfo carbonWindow) + { + WeakReference r = CarbonGLNative.WindowRefMap[carbonWindow.WindowRef]; + + if (r.IsAlive) + { + return (CarbonGLNative)r.Target; + } - if (r.IsAlive) - { - return (CarbonGLNative) r.Target; - } - else - return null; - } + else + return null; + } void MyAGLReportError(string function) { Agl.AglError err = Agl.GetError(); - + if (err != Agl.AglError.NoError) - throw new MacOSException((OSStatus)err, string.Format( - "AGL Error from function {0}: {1} {2}", - function, err, Agl.ErrorString(err))); + throw new MacOSException((OSStatus)err, string.Format("AGL Error from function {0}: {1} {2}", function, err, Agl.ErrorString(err))); } bool firstFullScreen = false; internal void SetFullScreen(CarbonWindowInfo info, out int width, out int height) { - CarbonGLNative wind = GetCarbonWindow(info); - - Debug.Print("Switching to full screen {0}x{1} on context {2}", - wind.TargetDisplayDevice.Width, wind.TargetDisplayDevice.Height, Handle.Handle); - - CG.DisplayCapture(GetQuartzDevice(info)); - Agl.aglSetFullScreen(Handle.Handle, wind.TargetDisplayDevice.Width, wind.TargetDisplayDevice.Height, 0, 0); - MakeCurrent(info); - - width = wind.TargetDisplayDevice.Width; - height = wind.TargetDisplayDevice.Height; - + CarbonGLNative wind = GetCarbonWindow(info); + + Debug.Print("Switching to full screen {0}x{1} on context {2}", wind.TargetDisplayDevice.Width, wind.TargetDisplayDevice.Height, Handle.Handle); + + CG.DisplayCapture(GetQuartzDevice(info)); + Agl.aglSetFullScreen(Handle.Handle, wind.TargetDisplayDevice.Width, wind.TargetDisplayDevice.Height, 0, 0); + MakeCurrent(info); + + width = wind.TargetDisplayDevice.Width; + height = wind.TargetDisplayDevice.Height; + // This is a weird hack to workaround a bug where the first time a context // is made fullscreen, we just end up with a blank screen. So we undo it as fullscreen // and redo it as fullscreen. - if (firstFullScreen == false) - { - firstFullScreen = true; - UnsetFullScreen(info); - SetFullScreen(info, out width, out height); - } - - mIsFullscreen = true; + if (firstFullScreen == false) + { + firstFullScreen = true; + UnsetFullScreen(info); + SetFullScreen(info, out width, out height); + } + + mIsFullscreen = true; } internal void UnsetFullScreen(CarbonWindowInfo windowInfo) { - Debug.Print("Unsetting AGL fullscreen."); + Debug.Print("Unsetting AGL fullscreen."); Agl.aglSetDrawable(Handle.Handle, IntPtr.Zero); - Agl.aglUpdateContext(Handle.Handle); - - CG.DisplayRelease(GetQuartzDevice(windowInfo)); - Debug.Print("Resetting drawable."); - SetDrawable(windowInfo); - - mIsFullscreen = false; + Agl.aglUpdateContext(Handle.Handle); + + CG.DisplayRelease(GetQuartzDevice(windowInfo)); + Debug.Print("Resetting drawable."); + SetDrawable(windowInfo); + + mIsFullscreen = false; } @@ -393,14 +408,14 @@ namespace OpenTK.Platform.MacOS { Debug.WriteLine("--> Resetting drawable. <--"); firstSwap = true; - SetDrawable(carbonWindow); - Update(carbonWindow); + SetDrawable(carbonWindow); + Update(carbonWindow); } - - Agl.aglSwapBuffers(Handle.Handle); - MyAGLReportError("aglSwapBuffers"); + + Agl.aglSwapBuffers(Handle.Handle); + MyAGLReportError("aglSwapBuffers"); } - + public override void MakeCurrent(IWindowInfo window) { if (Agl.aglSetCurrentContext(Handle.Handle) == false) @@ -409,24 +424,18 @@ namespace OpenTK.Platform.MacOS public override bool IsCurrent { - get - { - return (Handle.Handle == Agl.aglGetCurrentContext()); - } + get { return (Handle.Handle == Agl.aglGetCurrentContext()); } } public override bool VSync { - get - { - return mVSync; - } + get { return mVSync; } set { int intVal = value ? 1 : 0; - + Agl.aglSetInteger(Handle.Handle, Agl.ParameterNames.AGL_SWAP_INTERVAL, ref intVal); - + mVSync = value; } } @@ -449,34 +458,34 @@ namespace OpenTK.Platform.MacOS { if (IsDisposed || Handle.Handle == IntPtr.Zero) return; - + Debug.Print("Disposing of AGL context."); Agl.aglSetCurrentContext(IntPtr.Zero); - - //Debug.Print("Setting drawable to null for context {0}.", Handle.Handle); - //Agl.aglSetDrawable(Handle.Handle, IntPtr.Zero); - - // I do not know MacOS allows us to destroy a context from a separate thread, - // like the finalizer thread. It's untested, but worst case is probably - // an exception on application exit, which would be logged to the console. - Debug.Print("Destroying context"); + + //Debug.Print("Setting drawable to null for context {0}.", Handle.Handle); + //Agl.aglSetDrawable(Handle.Handle, IntPtr.Zero); + + // I do not know MacOS allows us to destroy a context from a separate thread, + // like the finalizer thread. It's untested, but worst case is probably + // an exception on application exit, which would be logged to the console. + Debug.Print("Destroying context"); if (Agl.aglDestroyContext(Handle.Handle) == true) { - Debug.Print("Context destruction completed successfully."); - Handle = ContextHandle.Zero; - return; + Debug.Print("Context destruction completed successfully."); + Handle = ContextHandle.Zero; + return; } - + // failed to destroy context. Debug.WriteLine("Failed to destroy context."); Debug.WriteLine(Agl.ErrorString(Agl.GetError())); - + // don't throw an exception from the finalizer thread. if (disposing) { throw new MacOSException((OSStatus)Agl.GetError(), Agl.ErrorString(Agl.GetError())); } - + IsDisposed = true; } @@ -484,7 +493,7 @@ namespace OpenTK.Platform.MacOS #region IGraphicsContextInternal Members - private const string Library = "libdl.dylib"; + private const string Library = "libdl.dylib"; [DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")] private static extern bool NSIsSymbolNameDefined(string s); @@ -498,14 +507,14 @@ namespace OpenTK.Platform.MacOS string fname = "_" + function; if (!NSIsSymbolNameDefined(fname)) return IntPtr.Zero; - + IntPtr symbol = NSLookupAndBindSymbol(fname); if (symbol != IntPtr.Zero) symbol = NSAddressOfSymbol(symbol); - + return symbol; } - + #endregion } } diff --git a/Source/OpenTK/Platform/MacOS/Application.cs b/Source/OpenTK/Platform/MacOS/Application.cs index 5c649787..c419a079 100644 --- a/Source/OpenTK/Platform/MacOS/Application.cs +++ b/Source/OpenTK/Platform/MacOS/Application.cs @@ -1,11 +1,31 @@ +#region License // -// -// xCSCarbon +// The Open Toolkit Library License // -// Created by Erik Ylvisaker on 3/17/08. -// Copyright 2008 __MyCompanyName__. All rights reserved. +// Copyright (c) 2006 - 2010 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 + +// Created by Erik Ylvisaker on 3/17/08. using System; using System.Collections.Generic; @@ -27,35 +47,36 @@ namespace OpenTK.Platform.MacOS.Carbon Initialize(); } - internal static void Initialize() + static internal void Initialize() { - if (mInitialized) return; + if (mInitialized) + return; API.AcquireRootMenu(); - + ConnectEvents(); - + API.Gestalt(GestaltSelector.SystemVersionMajor, out osMajor); API.Gestalt(GestaltSelector.SystemVersionMinor, out osMinor); API.Gestalt(GestaltSelector.SystemVersionBugFix, out osBugfix); - + Debug.Print("Running on Mac OS X {0}.{1}.{2}.", osMajor, osMinor, osBugfix); - - TransformProcessToForeground(); + + TransformProcessToForeground(); } - private static void TransformProcessToForeground() - { - Carbon.ProcessSerialNumber psn = new ProcessSerialNumber(); + private static void TransformProcessToForeground() + { + Carbon.ProcessSerialNumber psn = new ProcessSerialNumber(); + + Debug.Print("Setting process to be foreground application."); + + API.GetCurrentProcess(ref psn); + API.TransformProcessType(ref psn, ProcessApplicationTransformState.kProcessTransformToForegroundApplication); + API.SetFrontProcess(ref psn); + } - Debug.Print("Setting process to be foreground application."); - - API.GetCurrentProcess(ref psn); - API.TransformProcessType(ref psn, ProcessApplicationTransformState.kProcessTransformToForegroundApplication); - API.SetFrontProcess(ref psn); - } - - internal static CarbonGLNative WindowEventHandler + static internal CarbonGLNative WindowEventHandler { get { return eventHandler; } set { eventHandler = value; } @@ -63,33 +84,16 @@ namespace OpenTK.Platform.MacOS.Carbon static void ConnectEvents() { - EventTypeSpec[] eventTypes = new EventTypeSpec[] - { - new EventTypeSpec(EventClass.Application, AppEventKind.AppActivated), - new EventTypeSpec(EventClass.Application, AppEventKind.AppDeactivated), - new EventTypeSpec(EventClass.Application, AppEventKind.AppQuit), - - new EventTypeSpec(EventClass.Mouse, MouseEventKind.MouseDown), - new EventTypeSpec(EventClass.Mouse, MouseEventKind.MouseUp), - new EventTypeSpec(EventClass.Mouse, MouseEventKind.MouseMoved), - new EventTypeSpec(EventClass.Mouse, MouseEventKind.MouseDragged), - new EventTypeSpec(EventClass.Mouse, MouseEventKind.MouseEntered), - new EventTypeSpec(EventClass.Mouse, MouseEventKind.MouseExited), - new EventTypeSpec(EventClass.Mouse, MouseEventKind.WheelMoved), - - new EventTypeSpec(EventClass.Keyboard, KeyboardEventKind.RawKeyDown), - new EventTypeSpec(EventClass.Keyboard, KeyboardEventKind.RawKeyRepeat), - new EventTypeSpec(EventClass.Keyboard, KeyboardEventKind.RawKeyUp), - new EventTypeSpec(EventClass.Keyboard, KeyboardEventKind.RawKeyModifiersChanged), - - new EventTypeSpec(EventClass.AppleEvent, AppleEventKind.AppleEvent), - }; - + + EventTypeSpec[] eventTypes = new EventTypeSpec[] { new EventTypeSpec(EventClass.Application, AppEventKind.AppActivated), new EventTypeSpec(EventClass.Application, AppEventKind.AppDeactivated), new EventTypeSpec(EventClass.Application, AppEventKind.AppQuit), new EventTypeSpec(EventClass.Mouse, MouseEventKind.MouseDown), new EventTypeSpec(EventClass.Mouse, MouseEventKind.MouseUp), new EventTypeSpec(EventClass.Mouse, MouseEventKind.MouseMoved), new EventTypeSpec(EventClass.Mouse, MouseEventKind.MouseDragged), new EventTypeSpec(EventClass.Mouse, MouseEventKind.MouseEntered), new EventTypeSpec(EventClass.Mouse, MouseEventKind.MouseExited), new EventTypeSpec(EventClass.Mouse, MouseEventKind.WheelMoved), + + + new EventTypeSpec(EventClass.Keyboard, KeyboardEventKind.RawKeyDown), new EventTypeSpec(EventClass.Keyboard, KeyboardEventKind.RawKeyRepeat), new EventTypeSpec(EventClass.Keyboard, KeyboardEventKind.RawKeyUp), new EventTypeSpec(EventClass.Keyboard, KeyboardEventKind.RawKeyModifiersChanged), new EventTypeSpec(EventClass.AppleEvent, AppleEventKind.AppleEvent) }; + MacOSEventHandler handler = EventHandler; uppHandler = API.NewEventHandlerUPP(handler); - - API.InstallApplicationEventHandler( - uppHandler, eventTypes, IntPtr.Zero, IntPtr.Zero); + + API.InstallApplicationEventHandler(uppHandler, eventTypes, IntPtr.Zero, IntPtr.Zero); mInitialized = true; } @@ -100,28 +104,30 @@ namespace OpenTK.Platform.MacOS.Carbon switch (evt.EventClass) { - case EventClass.Application: - switch (evt.AppEventKind) - { - default: - return OSStatus.EventNotHandled; - } + case EventClass.Application: + switch (evt.AppEventKind) + { + default: + return OSStatus.EventNotHandled; + } - case EventClass.AppleEvent: - // only event here is the apple event. - Debug.Print("Processing apple event."); - API.ProcessAppleEvent(inEvent); - break; + + case EventClass.AppleEvent: + // only event here is the apple event. + Debug.Print("Processing apple event."); + API.ProcessAppleEvent(inEvent); + break; + + case EventClass.Keyboard: + case EventClass.Mouse: + if (WindowEventHandler != null) + { + return WindowEventHandler.DispatchEvent(inCaller, inEvent, evt, userData); + } - case EventClass.Keyboard: - case EventClass.Mouse: - if (WindowEventHandler != null) - { - return WindowEventHandler.DispatchEvent(inCaller, inEvent, evt, userData); - } - break; + break; } - + return OSStatus.EventNotHandled; } @@ -129,9 +135,9 @@ namespace OpenTK.Platform.MacOS.Carbon { window.Closed += MainWindowClosed; window.Visible = true; - + API.RunApplicationEventLoop(); - + window.Closed -= MainWindowClosed; } @@ -142,7 +148,7 @@ namespace OpenTK.Platform.MacOS.Carbon } - internal static void ProcessEvents() + static internal void ProcessEvents() { API.ProcessEvents(); } diff --git a/Source/OpenTK/Platform/MacOS/CarbonBindings/Agl.cs b/Source/OpenTK/Platform/MacOS/CarbonBindings/Agl.cs index 22b819df..03f4cb4a 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonBindings/Agl.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonBindings/Agl.cs @@ -1,11 +1,32 @@ +#region License // -// -// Agl.cs +// The Open Toolkit Library License // +// Copyright (c) 2006 - 2010 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 + // Created by Erik Ylvisaker on 3/17/08. -// Copyright 2008. All rights reserved. -// -// + using System; using System.Diagnostics; @@ -297,7 +318,7 @@ namespace OpenTK.Platform.MacOS [DllImport(agl)] internal static extern AGLPixelFormat aglChoosePixelFormat(IntPtr gdevs, int ndev, int []attribs); [DllImport(agl)] internal static extern void aglDestroyPixelFormat(AGLPixelFormat pix); [DllImport(agl)] internal static extern AGLPixelFormat aglNextPixelFormat(AGLPixelFormat pix); - [DllImport(agl)] static extern byte aglDescribePixelFormat(AGLPixelFormat pix, int attrib, out int value); + [DllImport(agl)] internal static extern bool aglDescribePixelFormat(AGLPixelFormat pix, PixelFormatAttribute attrib, out int value); [Obsolete("Use aglDisplaysOfPixelFormat instead.")] [DllImport(agl)] static extern AGLDevice *aglDevicesOfPixelFormat(AGLPixelFormat pix, int *ndevs); diff --git a/Source/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs b/Source/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs index 4ae61ca9..0cf7847e 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs @@ -1,11 +1,32 @@ +#region License // -// -// Carbon.cs +// The Open Toolkit Library License // +// Copyright (c) 2006 - 2010 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 + // Created by Erik Ylvisaker on 3/17/08. -// Copyright 2008. All rights reserved. -// -// + using System; using System.Runtime.InteropServices; @@ -99,6 +120,14 @@ namespace OpenTK.Platform.MacOS.Carbon { public float X; public float Y; + public HIPoint(float x, float y) + { + X = x; + Y = y; + } + public HIPoint(double x, double y) + : this((float)x, (float)y) + { } } [StructLayout(LayoutKind.Sequential)] internal struct HISize @@ -706,17 +735,31 @@ namespace OpenTK.Platform.MacOS.Carbon unsafe { HIPoint* parm = &point; - OSStatus result = API.GetEventParameter(inEvent, EventParamName.WindowMouseLocation, EventParamType.typeHIPoint, IntPtr.Zero, (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(HIPoint)), IntPtr.Zero, (IntPtr)parm); - pt = point; return result; } + } + static internal OSStatus GetEventMouseDelta(IntPtr inEvent, out HIPoint pt) + { + HIPoint point; + + unsafe + { + HIPoint* parm = &point; + OSStatus result = API.GetEventParameter(inEvent, + EventParamName.MouseDelta, EventParamType.typeHIPoint, IntPtr.Zero, + (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(HIPoint)), IntPtr.Zero, + (IntPtr)parm); + pt = point; + + return result; + } } static internal OSStatus GetEventWindowRef(IntPtr inEvent, out IntPtr windowRef) diff --git a/Source/OpenTK/Platform/MacOS/CarbonBindings/CoreFoundation.cs b/Source/OpenTK/Platform/MacOS/CarbonBindings/CoreFoundation.cs index 10a607f1..f6c9b156 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonBindings/CoreFoundation.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonBindings/CoreFoundation.cs @@ -1,3 +1,30 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 + using System; using System.Collections.Generic; using System.Runtime.InteropServices; @@ -5,6 +32,8 @@ using System.Text; namespace OpenTK.Platform.MacOS.Carbon { + using CFRunLoop = System.IntPtr; + struct CFArray { IntPtr arrayRef; @@ -112,5 +141,25 @@ namespace OpenTK.Platform.MacOS.Carbon kCFNumberCGFloatType = 16, kCFNumberMaxType = 16 }; + + public enum CFRunLoopExitReason + { + Finished = 1, + Stopped = 2, + TimedOut = 3, + HandledSource = 4 + } + + public static readonly IntPtr RunLoopModeDefault = CF.CFSTR("kCFRunLoopDefaultMode"); + + [DllImport(appServices)] + internal static extern CFRunLoop CFRunLoopGetCurrent(); + + [DllImport(appServices)] + internal static extern CFRunLoop CFRunLoopGetMain(); + + [DllImport(appServices)] + internal static extern CFRunLoopExitReason CFRunLoopRunInMode( + IntPtr cfstrMode, double interval, bool returnAfterSourceHandled); } } diff --git a/Source/OpenTK/Platform/MacOS/CarbonBindings/MacOSKeys.cs b/Source/OpenTK/Platform/MacOS/CarbonBindings/MacOSKeys.cs index 90868408..b7fab61b 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonBindings/MacOSKeys.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonBindings/MacOSKeys.cs @@ -1,3 +1,30 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 + using System; using System.Collections.Generic; using System.Text; diff --git a/Source/OpenTK/Platform/MacOS/CarbonBindings/QuartzDisplayServicesAPI.cs b/Source/OpenTK/Platform/MacOS/CarbonBindings/QuartzDisplayServicesAPI.cs index d241a3d6..c21d4159 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonBindings/QuartzDisplayServicesAPI.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonBindings/QuartzDisplayServicesAPI.cs @@ -1,10 +1,38 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 + using System; using System.Runtime.InteropServices; using System.Diagnostics; - namespace OpenTK.Platform.MacOS.Carbon { + using CGDirectDisplayID = System.IntPtr; + // Quartz Display services used here are available in MacOS X 10.3 and later. enum CGDisplayErr @@ -12,6 +40,21 @@ namespace OpenTK.Platform.MacOS.Carbon } + enum CGError + { + Success = 0, + Failure = 1000, + IllegalArgument = 1001, + InvalidConnection = 1002, + InvalidContext = 1003, + CannotComplete = 1004, + NotImplemented = 1006, + RangeCheck = 1007, + TypeCheck = 1008, + InvalidOperation = 1010, + NoneAvailable = 1011, + } + internal static class CG { const string appServices = "/System/Library/Frameworks/ApplicationServices.framework/Versions/Current/ApplicationServices"; @@ -50,5 +93,22 @@ namespace OpenTK.Platform.MacOS.Carbon [DllImport(appServices, EntryPoint = "CGDisplaySwitchToMode")] internal static extern IntPtr DisplaySwitchToMode(IntPtr display, IntPtr displayMode); + [DllImport(appServices, EntryPoint = "CGWarpMouseCursorPosition")] + internal static extern CGError WarpMouseCursorPosition(HIPoint newCursorPosition); + + [DllImport(appServices, EntryPoint = "CGCursorIsVisible")] + internal static extern bool CursorIsVisible(); + + [DllImport(appServices, EntryPoint = "CGDisplayShowCursor")] + internal static extern CGError DisplayShowCursor(CGDirectDisplayID display); + + [DllImport(appServices, EntryPoint = "CGDisplayHideCursor")] + internal static extern CGError DisplayHideCursor(CGDirectDisplayID display); + + [DllImport(appServices, EntryPoint = "CGAssociateMouseAndMouseCursorPosition")] + internal static extern CGError AssociateMouseAndMouseCursorPosition(bool connected); + + [DllImport(appServices, EntryPoint="CGSetLocalEventsSuppressionInterval")] + internal static extern CGError SetLocalEventsSuppressionInterval(double seconds); } } diff --git a/Source/OpenTK/Platform/MacOS/CarbonBindings/SpeechChannel.cs b/Source/OpenTK/Platform/MacOS/CarbonBindings/SpeechChannel.cs index cc84f25c..d96fce72 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonBindings/SpeechChannel.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonBindings/SpeechChannel.cs @@ -1,3 +1,30 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 + using System; using System.Collections.Generic; using System.Diagnostics; diff --git a/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs b/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs index aa483365..c1c8eaa5 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs @@ -2,7 +2,7 @@ // // The Open Toolkit Library License // -// Copyright (c) 2006 - 2009 the Open Toolkit library. +// Copyright (c) 2006 - 2010 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 @@ -25,6 +25,8 @@ // #endregion +// Created by Erik Ylvisaker on 3/17/08. + using System; using System.Collections.Generic; using System.ComponentModel; @@ -50,10 +52,10 @@ namespace OpenTK.Platform.MacOS string title = "OpenTK Window"; Rectangle bounds, clientRectangle; - Rectangle windowedBounds; + Rectangle windowedBounds; bool mIsDisposed = false; - bool mExists = true; - DisplayDevice mDisplayDevice; + bool mExists = true; + DisplayDevice mDisplayDevice; WindowAttributes mWindowAttrib; WindowClass mWindowClass; @@ -62,39 +64,47 @@ namespace OpenTK.Platform.MacOS private WindowBorder windowBorder = WindowBorder.Resizable; private WindowState windowState = WindowState.Normal; - static Dictionary mWindows = new Dictionary(); + static Dictionary mWindows = + new Dictionary(new IntPtrEqualityComparer()); - KeyPressEventArgs mKeyPressArgs = new KeyPressEventArgs((char)0); + KeyPressEventArgs mKeyPressArgs = new KeyPressEventArgs((char)0); - bool mMouseIn = false; - bool mIsActive = false; + bool mMouseIn = false; + bool mIsActive = false; - Icon mIcon; + Icon mIcon; + + // Used to accumulate mouse motion when the cursor is hidden. + float mouse_rel_x; + float mouse_rel_y; #endregion - #region AGL Device Hack + #region AGL Device Hack - static internal Dictionary WindowRefMap { get { return mWindows; } } - internal DisplayDevice TargetDisplayDevice { get { return mDisplayDevice; } } + static internal Dictionary WindowRefMap + { + get { return mWindows; } + } + internal DisplayDevice TargetDisplayDevice + { + get { return mDisplayDevice; } + } - #endregion + #endregion - #region Constructors + #region Constructors - static CarbonGLNative() + static CarbonGLNative() { Application.Initialize(); } - CarbonGLNative() - : this(WindowClass.Document, - WindowAttributes.StandardDocument | - WindowAttributes.StandardHandler | - WindowAttributes.InWindowMenu | - WindowAttributes.LiveResize) - { } - + CarbonGLNative() : this(WindowClass.Document, + WindowAttributes.StandardDocument | WindowAttributes.StandardHandler | + WindowAttributes.InWindowMenu | WindowAttributes.LiveResize) + { + } CarbonGLNative(WindowClass @class, WindowAttributes attrib) { @@ -102,14 +112,15 @@ namespace OpenTK.Platform.MacOS mWindowAttrib = attrib; } - public CarbonGLNative(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) + public CarbonGLNative(int x, int y, int width, int height, string title, + GraphicsMode mode, GameWindowFlags options, DisplayDevice device) { - CreateNativeWindow(WindowClass.Document, + CreateNativeWindow(WindowClass.Document, WindowAttributes.StandardDocument | WindowAttributes.StandardHandler | WindowAttributes.InWindowMenu | WindowAttributes.LiveResize, new Rect((short)x, (short)y, (short)width, (short)height)); - - mDisplayDevice = device; + + mDisplayDevice = device; } #endregion @@ -129,21 +140,24 @@ namespace OpenTK.Platform.MacOS Debug.Print("Disposing of CarbonGLNative window."); - API.DisposeWindow(window.WindowRef); - + CursorVisible = true; + API.DisposeWindow(window.WindowRef); mIsDisposed = true; - mExists = false; + mExists = false; + + CG.SetLocalEventsSuppressionInterval(0.25); if (disposing) { mWindows.Remove(window.WindowRef); - + window.Dispose(); window = null; } DisposeUPP(); + Disposed(this, EventArgs.Empty); } ~CarbonGLNative() @@ -162,7 +176,7 @@ namespace OpenTK.Platform.MacOS //API.RemoveEventHandler(uppHandler); //API.DisposeEventHandlerUPP(uppHandler); } - + uppHandler = IntPtr.Zero; } @@ -170,29 +184,29 @@ namespace OpenTK.Platform.MacOS { Debug.Print("Creating window..."); Debug.Indent(); - + IntPtr windowRef = API.CreateNewWindow(@class, attrib, r); API.SetWindowTitle(windowRef, title); - + window = new CarbonWindowInfo(windowRef, true, false); - + SetLocation(r.X, r.Y); SetSize(r.Width, r.Height); - + Debug.Unindent(); Debug.Print("Created window."); - + mWindows.Add(windowRef, new WeakReference(this)); - + LoadSize(); - + Rect titleSize = API.GetWindowBounds(window.WindowRef, WindowRegionCode.TitleBarRegion); mTitlebarHeight = titleSize.Height; - + Debug.Print("Titlebar size: {0}", titleSize); - + ConnectEvents(); - + System.Diagnostics.Debug.Print("Attached window events."); } @@ -205,8 +219,8 @@ namespace OpenTK.Platform.MacOS new EventTypeSpec(EventClass.Window, WindowEventKind.WindowClose), new EventTypeSpec(EventClass.Window, WindowEventKind.WindowClosed), new EventTypeSpec(EventClass.Window, WindowEventKind.WindowBoundsChanged), - new EventTypeSpec(EventClass.Window, WindowEventKind.WindowActivate), - new EventTypeSpec(EventClass.Window, WindowEventKind.WindowDeactivate), + new EventTypeSpec(EventClass.Window, WindowEventKind.WindowActivate), + new EventTypeSpec(EventClass.Window, WindowEventKind.WindowDeactivate), //new EventTypeSpec(EventClass.Mouse, MouseEventKind.MouseDown), //new EventTypeSpec(EventClass.Mouse, MouseEventKind.MouseUp), @@ -221,12 +235,13 @@ namespace OpenTK.Platform.MacOS //new EventTypeSpec(EventClass.Keyboard, KeyboardEventKind.RawKeyUp), //new EventTypeSpec(EventClass.Keyboard, KeyboardEventKind.RawKeyModifiersChanged), }; - + MacOSEventHandler handler = EventHandler; uppHandler = API.NewEventHandlerUPP(handler); - - API.InstallWindowEventHandler(window.WindowRef, uppHandler, eventTypes, window.WindowRef, IntPtr.Zero); - + + API.InstallWindowEventHandler(window.WindowRef, uppHandler, eventTypes, + window.WindowRef, IntPtr.Zero); + Application.WindowEventHandler = this; } @@ -238,7 +253,7 @@ namespace OpenTK.Platform.MacOS void Show() { IntPtr parent = IntPtr.Zero; - + API.ShowWindow(window.WindowRef); API.RepositionWindow(window.WindowRef, parent, WindowPositionMethod); API.SelectWindow(window.WindowRef); @@ -252,29 +267,29 @@ namespace OpenTK.Platform.MacOS internal void SetFullscreen(AglContext context) { windowedBounds = bounds; - - int width, height; - - context.SetFullScreen(window, out width, out height); - - Debug.Print("Prev Size: {0}, {1}", Width, Height); - clientRectangle.Size = new Size(width, height); - Debug.Print("New Size: {0}, {1}", Width, Height); - - // TODO: if we go full screen we need to make this use the device specified. - bounds = mDisplayDevice.Bounds; - - windowState = WindowState.Fullscreen; + int width, height; + + context.SetFullScreen(window, out width, out height); + + Debug.Print("Prev Size: {0}, {1}", Width, Height); + clientRectangle.Size = new Size(width, height); + Debug.Print("New Size: {0}, {1}", Width, Height); + + // TODO: if we go full screen we need to make this use the device specified. + bounds = mDisplayDevice.Bounds; + + + windowState = WindowState.Fullscreen; } internal void UnsetFullscreen(AglContext context) { context.UnsetFullScreen(window); - - Debug.Print("Telling Carbon to reset window state to " + windowState.ToString()); - SetCarbonWindowState(); - + + Debug.Print("Telling Carbon to reset window state to " + windowState.ToString()); + SetCarbonWindowState(); + SetSize((short)windowedBounds.Width, (short)windowedBounds.Height); } @@ -309,45 +324,30 @@ namespace OpenTK.Platform.MacOS protected static OSStatus EventHandler(IntPtr inCaller, IntPtr inEvent, IntPtr userData) { - // bail out if the window passed in is not actually our window. - // I think this happens if using winforms with a GameWindow sometimes. if (mWindows.ContainsKey(userData) == false) + { + // Bail out if the window passed in is not actually our window. + // I think this happens if using winforms with a GameWindow sometimes. return OSStatus.EventNotHandled; + } WeakReference reference = mWindows[userData]; - - // bail out if the CarbonGLNative window has been garbage collected. if (reference.IsAlive == false) { + // Bail out if the CarbonGLNative window has been garbage collected. mWindows.Remove(userData); return OSStatus.EventNotHandled; } - CarbonGLNative window = (CarbonGLNative)reference.Target; - EventInfo evt = new EventInfo(inEvent); - - //Debug.Print("Processing {0} event for {1}.", evt, window.window); - + CarbonGLNative window = (CarbonGLNative)reference.Target; if (window == null) { Debug.WriteLine("Window for event not found."); return OSStatus.EventNotHandled; } - switch (evt.EventClass) - { - case EventClass.Window: - return window.ProcessWindowEvent(inCaller, inEvent, evt, userData); - - case EventClass.Mouse: - return window.ProcessMouseEvent(inCaller, inEvent, evt, userData); - - case EventClass.Keyboard: - return window.ProcessKeyboardEvent(inCaller, inEvent, evt, userData); - - default: - return OSStatus.EventNotHandled; - } + EventInfo evt = new EventInfo(inEvent); + return window.DispatchEvent(inCaller, inEvent, evt, userData); } private OSStatus ProcessKeyboardEvent(IntPtr inCaller, IntPtr inEvent, EventInfo evt, IntPtr userData) @@ -356,278 +356,284 @@ namespace OpenTK.Platform.MacOS MacOSKeyCode code = (MacOSKeyCode)0; char charCode = '\0'; - //Debug.Print("Processing keyboard event {0}", evt.KeyboardEventKind); - - switch (evt.KeyboardEventKind) - { - case KeyboardEventKind.RawKeyDown: - case KeyboardEventKind.RawKeyRepeat: - case KeyboardEventKind.RawKeyUp: - GetCharCodes(inEvent, out code, out charCode); - mKeyPressArgs.KeyChar = charCode; - break; - } + switch (evt.KeyboardEventKind) + { + case KeyboardEventKind.RawKeyDown: + case KeyboardEventKind.RawKeyRepeat: + case KeyboardEventKind.RawKeyUp: + GetCharCodes(inEvent, out code, out charCode); + mKeyPressArgs.KeyChar = charCode; + break; + } switch (evt.KeyboardEventKind) { case KeyboardEventKind.RawKeyRepeat: - InputDriver.Keyboard[0].KeyRepeat = true; - goto case KeyboardEventKind.RawKeyDown; + if (InputDriver.Keyboard[0].KeyRepeat) + goto case KeyboardEventKind.RawKeyDown; + break; case KeyboardEventKind.RawKeyDown: - OnKeyPress(mKeyPressArgs); - InputDriver.Keyboard[0][Keymap[code]] = true; + { + OpenTK.Input.Key key; + if (Keymap.TryGetValue(code, out key)) + { + InputDriver.Keyboard[0][key] = true; + OnKeyPress(mKeyPressArgs); + } return OSStatus.NoError; + } case KeyboardEventKind.RawKeyUp: - InputDriver.Keyboard[0][Keymap[code]] = false; - - return OSStatus.NoError; + { + OpenTK.Input.Key key; + if (Keymap.TryGetValue(code, out key)) + { + InputDriver.Keyboard[0][key] = false; + } + return OSStatus.NoError; + } case KeyboardEventKind.RawKeyModifiersChanged: ProcessModifierKey(inEvent); - return OSStatus.NoError; - - default: - return OSStatus.EventNotHandled; + return OSStatus.NoError; } - + return OSStatus.EventNotHandled; } private OSStatus ProcessWindowEvent(IntPtr inCaller, IntPtr inEvent, EventInfo evt, IntPtr userData) { System.Diagnostics.Debug.Assert(evt.EventClass == EventClass.Window); - + switch (evt.WindowEventKind) { - case WindowEventKind.WindowClose: - CancelEventArgs cancel = new CancelEventArgs(); - OnClosing(cancel); - - if (cancel.Cancel) - return OSStatus.NoError; - else - return OSStatus.EventNotHandled; - - case WindowEventKind.WindowClosed: - mExists = false; - OnClosed(); - + case WindowEventKind.WindowClose: + CancelEventArgs cancel = new CancelEventArgs(); + OnClosing(cancel); + + if (cancel.Cancel) return OSStatus.NoError; - - case WindowEventKind.WindowBoundsChanged: - int thisWidth = Width; - int thisHeight = Height; - - LoadSize(); - - if (thisWidth != Width || thisHeight != Height) - OnResize(); - - return OSStatus.EventNotHandled; - - case WindowEventKind.WindowActivate: - OnActivate(); - return OSStatus.EventNotHandled; - - case WindowEventKind.WindowDeactivate: - OnDeactivate(); - return OSStatus.EventNotHandled; - - default: - Debug.Print("{0}", evt); - + else return OSStatus.EventNotHandled; + + case WindowEventKind.WindowClosed: + mExists = false; + OnClosed(); + + return OSStatus.NoError; + + case WindowEventKind.WindowBoundsChanged: + int thisWidth = Width; + int thisHeight = Height; + int thisX = X; + int thisY = Y; + + LoadSize(); + + if (thisX != X || thisY != Y) + Move(this, EventArgs.Empty); + + if (thisWidth != Width || thisHeight != Height) + Resize(this, EventArgs.Empty); + + return OSStatus.EventNotHandled; + + case WindowEventKind.WindowActivate: + OnActivate(); + return OSStatus.EventNotHandled; + + case WindowEventKind.WindowDeactivate: + OnDeactivate(); + return OSStatus.EventNotHandled; + default: + + Debug.Print("{0}", evt); + + return OSStatus.EventNotHandled; } } + protected OSStatus ProcessMouseEvent(IntPtr inCaller, IntPtr inEvent, EventInfo evt, IntPtr userData) { System.Diagnostics.Debug.Assert(evt.EventClass == EventClass.Mouse); MouseButton button = MouseButton.Primary; HIPoint pt = new HIPoint(); - HIPoint screenLoc = new HIPoint(); + HIPoint screenLoc = new HIPoint(); - OSStatus err = API.GetEventMouseLocation(inEvent, out screenLoc); + IntPtr thisEventWindow; + API.GetEventWindowRef(inEvent, out thisEventWindow); + OSStatus err = API.GetEventMouseLocation(inEvent, out screenLoc); if (this.windowState == WindowState.Fullscreen) { - pt = screenLoc; + pt = screenLoc; + } + else if (CursorVisible) + { + err = API.GetEventWindowMouseLocation(inEvent, out pt); + pt.Y -= mTitlebarHeight; } else { - err = API.GetEventWindowMouseLocation(inEvent, out pt); + err = API.GetEventMouseDelta(inEvent, out pt); + pt.X += mouse_rel_x; + pt.Y += mouse_rel_y; + pt = ConfineMouseToWindow(thisEventWindow, pt); + ResetMouseToWindowCenter(); + mouse_rel_x = pt.X; + mouse_rel_y = pt.Y; } - - if (err != OSStatus.NoError) + + if (err != OSStatus.NoError && err != OSStatus.EventParameterNotFound) { // this error comes up from the application event handler. - if (err != OSStatus.EventParameterNotFound) - { - throw new MacOSException(err); - } + throw new MacOSException(err); } - - Point mousePosInClient = new Point((int)pt.X, (int)pt.Y); - if (this.windowState != WindowState.Fullscreen) - { - mousePosInClient.Y -= mTitlebarHeight; - } - - // check for enter/leave events - IntPtr thisEventWindow; - API.GetEventWindowRef(inEvent, out thisEventWindow); - CheckEnterLeaveEvents(thisEventWindow, mousePosInClient); - + + Point mousePosInClient = new Point((int)pt.X, (int)pt.Y); + CheckEnterLeaveEvents(thisEventWindow, mousePosInClient); + switch (evt.MouseEventKind) { case MouseEventKind.MouseDown: - button = API.GetEventMouseButton(inEvent); - - switch (button) - { - case MouseButton.Primary: - InputDriver.Mouse[0][OpenTK.Input.MouseButton.Left] = true; - break; - - case MouseButton.Secondary: - InputDriver.Mouse[0][OpenTK.Input.MouseButton.Right] = true; - break; - - case MouseButton.Tertiary: - InputDriver.Mouse[0][OpenTK.Input.MouseButton.Middle] = true; - break; - } - - - return OSStatus.NoError; - case MouseEventKind.MouseUp: - button = API.GetEventMouseButton(inEvent); + button = API.GetEventMouseButton(inEvent); + bool pressed = evt.MouseEventKind == MouseEventKind.MouseDown; switch (button) { case MouseButton.Primary: - InputDriver.Mouse[0][OpenTK.Input.MouseButton.Left] = false; + InputDriver.Mouse[0][OpenTK.Input.MouseButton.Left] = pressed; break; case MouseButton.Secondary: - InputDriver.Mouse[0][OpenTK.Input.MouseButton.Right] = false; + InputDriver.Mouse[0][OpenTK.Input.MouseButton.Right] = pressed; break; case MouseButton.Tertiary: - InputDriver.Mouse[0][OpenTK.Input.MouseButton.Middle] = false; + InputDriver.Mouse[0][OpenTK.Input.MouseButton.Middle] = pressed; break; } + return OSStatus.NoError; - button = API.GetEventMouseButton(inEvent); - - return OSStatus.NoError; - - case MouseEventKind.WheelMoved: - - int delta = API.GetEventMouseWheelDelta(inEvent) / 3; - - InputDriver.Mouse[0].Wheel += delta; - - return OSStatus.NoError; + case MouseEventKind.WheelMoved: + float delta = API.GetEventMouseWheelDelta(inEvent); + InputDriver.Mouse[0].WheelPrecise += delta; + return OSStatus.NoError; case MouseEventKind.MouseMoved: case MouseEventKind.MouseDragged: - - //Debug.Print("Mouse Location: {0}, {1}", pt.X, pt.Y); - - if (this.windowState == WindowState.Fullscreen) - { - if (mousePosInClient.X != InputDriver.Mouse[0].X || - mousePosInClient.Y != InputDriver.Mouse[0].Y) - { - InputDriver.Mouse[0].Position = mousePosInClient; - } - } - else - { - // ignore clicks in the title bar - if (pt.Y < 0) - return OSStatus.EventNotHandled; - - if (mousePosInClient.X != InputDriver.Mouse[0].X || - mousePosInClient.Y != InputDriver.Mouse[0].Y) - { - InputDriver.Mouse[0].Position = mousePosInClient; - } - } + if (this.windowState == WindowState.Fullscreen) + { + if (mousePosInClient.X != InputDriver.Mouse[0].X || mousePosInClient.Y != InputDriver.Mouse[0].Y) + { + InputDriver.Mouse[0].Position = mousePosInClient; + } + } + else + { + // ignore clicks in the title bar + if (pt.Y < 0) + return OSStatus.EventNotHandled; + if (mousePosInClient.X != InputDriver.Mouse[0].X || mousePosInClient.Y != InputDriver.Mouse[0].Y) + { + InputDriver.Mouse[0].Position = mousePosInClient; + } + } return OSStatus.EventNotHandled; default: Debug.Print("{0}", evt); - return OSStatus.EventNotHandled; } } - private void CheckEnterLeaveEvents(IntPtr eventWindowRef, Point pt) - { - if (window == null) - return; + void ResetMouseToWindowCenter() + { + OpenTK.Input.Mouse.SetPosition( + (Bounds.Left + Bounds.Right) / 2, + (Bounds.Top + Bounds.Bottom) / 2); + } - bool thisIn = eventWindowRef == window.WindowRef; + private void CheckEnterLeaveEvents(IntPtr eventWindowRef, Point pt) + { + if (window == null) + return; - if (pt.Y < 0) - thisIn = false; + bool thisIn = eventWindowRef == window.WindowRef; - if (thisIn != mMouseIn) - { - mMouseIn = thisIn; + if (pt.Y < 0) + thisIn = false; - if (mMouseIn) - OnMouseEnter(); - else - OnMouseLeave(); - } - } + if (thisIn != mMouseIn) + { + mMouseIn = thisIn; + + if (mMouseIn) + OnMouseEnter(); + else + OnMouseLeave(); + } + } + + // Point in client (window) coordinates + private HIPoint ConfineMouseToWindow(IntPtr window, HIPoint client) + { + if (client.X < 0) + client.X = 0; + if (client.X >= Width) + client.X = Width - 1; + if (client.Y < 0) + client.Y = 0; + if (client.Y >= Height) + client.Y = Height - 1; + + return client; + } private static void GetCharCodes(IntPtr inEvent, out MacOSKeyCode code, out char charCode) { code = API.GetEventKeyboardKeyCode(inEvent); charCode = API.GetEventKeyboardChar(inEvent); } + private void ProcessModifierKey(IntPtr inEvent) { MacOSKeyModifiers modifiers = API.GetEventKeyModifiers(inEvent); - + bool caps = (modifiers & MacOSKeyModifiers.CapsLock) != 0 ? true : false; bool control = (modifiers & MacOSKeyModifiers.Control) != 0 ? true : false; bool command = (modifiers & MacOSKeyModifiers.Command) != 0 ? true : false; bool option = (modifiers & MacOSKeyModifiers.Option) != 0 ? true : false; bool shift = (modifiers & MacOSKeyModifiers.Shift) != 0 ? true : false; - + Debug.Print("Modifiers Changed: {0}", modifiers); - + Input.KeyboardDevice keyboard = InputDriver.Keyboard[0]; - + if (keyboard[OpenTK.Input.Key.AltLeft] ^ option) keyboard[OpenTK.Input.Key.AltLeft] = option; - + if (keyboard[OpenTK.Input.Key.ShiftLeft] ^ shift) keyboard[OpenTK.Input.Key.ShiftLeft] = shift; - + if (keyboard[OpenTK.Input.Key.WinLeft] ^ command) keyboard[OpenTK.Input.Key.WinLeft] = command; - + if (keyboard[OpenTK.Input.Key.ControlLeft] ^ control) keyboard[OpenTK.Input.Key.ControlLeft] = control; - + if (keyboard[OpenTK.Input.Key.CapsLock] ^ caps) keyboard[OpenTK.Input.Key.CapsLock] = caps; - + } Rect GetRegion() { Rect retval = API.GetWindowBounds(window.WindowRef, WindowRegionCode.ContentRegion); - + return retval; } @@ -635,7 +641,7 @@ namespace OpenTK.Platform.MacOS { if (windowState == WindowState.Fullscreen) return; - + API.MoveWindow(window.WindowRef, x, y, false); } @@ -643,72 +649,55 @@ namespace OpenTK.Platform.MacOS { 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); + + // 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.WindowRef, width, height, true); } - void SetClientSize(short width, short height) - { - if (WindowState == WindowState.Fullscreen) - return; - - API.SizeWindow(window.WindowRef, width, height, true); - } - - protected void OnResize() + void SetClientSize(short width, short height) { - LoadSize(); - - if (Resize != null) - { - Resize(this, EventArgs.Empty); - } + if (WindowState == WindowState.Fullscreen) + return; + + API.SizeWindow(window.WindowRef, width, height, true); } private void LoadSize() { if (WindowState == WindowState.Fullscreen) return; - - Rect r = API.GetWindowBounds(window.WindowRef, WindowRegionCode.StructureRegion); - bounds = new Rectangle(r.X, r.Y, r.Width, r.Height); - - r = API.GetWindowBounds(window.WindowRef, WindowRegionCode.GlobalPortRegion); - clientRectangle = new Rectangle(0, 0, r.Width, r.Height); + + Rect r = API.GetWindowBounds(window.WindowRef, WindowRegionCode.StructureRegion); + bounds = new Rectangle(r.X, r.Y, r.Width, r.Height); + + r = API.GetWindowBounds(window.WindowRef, WindowRegionCode.GlobalPortRegion); + clientRectangle = new Rectangle(0, 0, r.Width, r.Height); } #endregion - #region INativeWindow Members + #region INativeWindow Members - public void ProcessEvents() + public void ProcessEvents() { Application.ProcessEvents(); } public Point PointToClient(Point point) { - IntPtr handle = window.WindowRef; - Rect r = Carbon.API.GetWindowBounds(window.WindowRef, WindowRegionCode.ContentRegion); - Debug.Print("Rect: {0}", r); - return new Point(point.X - r.X, point.Y - r.Y); } + public Point PointToScreen(Point point) { - IntPtr handle = window.WindowRef; - - Rect r = Carbon.API.GetWindowBounds(window.WindowRef, WindowRegionCode.ContentRegion); - Debug.Print("Rect: {0}", r); - - return new Point(point.X + r.X, point.Y + r.Y); + Rect r = Carbon.API.GetWindowBounds(window.WindowRef, WindowRegionCode.ContentRegion); + return new Point(point.X + r.X, point.Y + r.Y); } public bool Exists @@ -728,82 +717,88 @@ namespace OpenTK.Platform.MacOS public OpenTK.Input.IInputDriver InputDriver { - get - { - return mInputDriver; - } + get { return mInputDriver; } } - public Icon Icon { - get { return mIcon; } - set { - SetIcon(value); - } + get { return mIcon; } + set + { + if (value != Icon) + { + SetIcon(value); + mIcon = value; + IconChanged(this, EventArgs.Empty); + } + } } - private void SetIcon(Icon icon) - { - // The code for this function was adapted from Mono's - // XplatUICarbon implementation, written by Geoff Norton - // http://anonsvn.mono-project.com/viewvc/trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/XplatUICarbon.cs?view=markup&pathrev=136932 - if (icon == null) - { - API.RestoreApplicationDockTileImage(); - } - else - { - Bitmap bitmap; - int size; - IntPtr[] data; - int index; + private void SetIcon(Icon icon) + { + // The code for this function was adapted from Mono's + // XplatUICarbon implementation, written by Geoff Norton + // http://anonsvn.mono-project.com/viewvc/trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/XplatUICarbon.cs?view=markup&pathrev=136932 + if (icon == null) + { + API.RestoreApplicationDockTileImage(); + } - bitmap = new Bitmap(128, 128); - using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmap)) - { - g.DrawImage(icon.ToBitmap(), 0, 0, 128, 128); - } - index = 0; - size = bitmap.Width * bitmap.Height; - data = new IntPtr[size]; + else + { + Bitmap bitmap; + int size; + IntPtr[] data; + int index; + + bitmap = new Bitmap(128, 128); + using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmap)) + { + g.DrawImage(icon.ToBitmap(), 0, 0, 128, 128); + } + index = 0; + size = bitmap.Width * bitmap.Height; + data = new IntPtr[size]; + + for (int y = 0; y < bitmap.Height; y++) + { + for (int x = 0; x < bitmap.Width; x++) + { + int pixel = bitmap.GetPixel(x, y).ToArgb(); + if (BitConverter.IsLittleEndian) + { + byte a = (byte)((pixel >> 24) & 0xFF); + byte r = (byte)((pixel >> 16) & 0xFF); + byte g = (byte)((pixel >> 8) & 0xFF); + byte b = (byte)(pixel & 0xFF); + data[index++] = (IntPtr)(a + (r << 8) + (g << 16) + (b << 24)); + } - for (int y = 0; y < bitmap.Height; y++) - { - for (int x = 0; x < bitmap.Width; x++) - { - int pixel = bitmap.GetPixel(x, y).ToArgb(); - if (BitConverter.IsLittleEndian) - { - byte a = (byte)((pixel >> 24) & 0xFF); - byte r = (byte)((pixel >> 16) & 0xFF); - byte g = (byte)((pixel >> 8) & 0xFF); - byte b = (byte)(pixel & 0xFF); - data[index++] = (IntPtr)(a + (r << 8) + (g << 16) + (b << 24)); - } - else - { - data[index++] = (IntPtr)pixel; - } - } - } - - IntPtr provider = API.CGDataProviderCreateWithData(IntPtr.Zero, data, size * 4, IntPtr.Zero); - IntPtr image = API.CGImageCreate(128, 128, 8, 32, 4 * 128, API.CGColorSpaceCreateDeviceRGB(), 4, provider, IntPtr.Zero, 0, 0); - API.SetApplicationDockTileImage(image); - } - } + else + { + data[index++] = (IntPtr)pixel; + } + } + } + + IntPtr provider = API.CGDataProviderCreateWithData(IntPtr.Zero, data, size * 4, IntPtr.Zero); + IntPtr image = API.CGImageCreate(128, 128, 8, 32, 4 * 128, API.CGColorSpaceCreateDeviceRGB(), 4, provider, IntPtr.Zero, 0, + 0); + API.SetApplicationDockTileImage(image); + } + } public string Title { - get - { - return title; - } + get { return title; } set { - API.SetWindowTitle(window.WindowRef, value); - title = value; + if (value != Title) + { + API.SetWindowTitle(window.WindowRef, value); + title = value; + TitleChanged(this, EventArgs.Empty); + } } } @@ -812,25 +807,27 @@ namespace OpenTK.Platform.MacOS get { return API.IsWindowVisible(window.WindowRef); } set { - if (value && Visible == false) - Show(); - else - Hide(); + if (value != Visible) + { + if (value) + Show(); + else + Hide(); + + VisibleChanged(this, EventArgs.Empty); + } } } public bool Focused { - get { return this.mIsActive; } + get { return this.mIsActive; } } public Rectangle Bounds { - get - { - - return bounds; - } + + get { return bounds; } set { Location = value.Location; @@ -840,26 +837,14 @@ namespace OpenTK.Platform.MacOS public Point Location { - get - { - return Bounds.Location; - } - set - { - SetLocation((short)value.X, (short)value.Y); - } + get { return Bounds.Location; } + set { SetLocation((short)value.X, (short)value.Y); } } public Size Size { - get - { - return bounds.Size; - } - set - { - SetSize((short)value.Width, (short)value.Height); - } + get { return bounds.Size; } + set { SetSize((short)value.Width, (short)value.Height); } } public int Width @@ -876,66 +861,65 @@ namespace OpenTK.Platform.MacOS public int X { - get - { - return ClientRectangle.X; - } - set - { - Location = new Point(value, Y); - } + get { return ClientRectangle.X; } + set { Location = new Point(value, Y); } } public int Y { - get - { - return ClientRectangle.Y; - } - set - { - Location = new Point(X, value); - } + get { return ClientRectangle.Y; } + set { Location = new Point(X, value); } } public Rectangle ClientRectangle { - get - { - return clientRectangle; - } - set - { - // just set the size, and ignore the location value. - // this is the behavior of the Windows WinGLNative. - ClientSize = value.Size; - } + get { return clientRectangle; } +// just set the size, and ignore the location value. +// this is the behavior of the Windows WinGLNative. + set { ClientSize = value.Size; } } public Size ClientSize { - get - { - return clientRectangle.Size; - } + get { return clientRectangle.Size; } set { - API.SizeWindow(window.WindowRef, (short)value.Width, (short)value.Height, true); - OnResize(); + API.SizeWindow(window.WindowRef, (short)value.Width, (short)value.Height, true); + LoadSize(); + Resize(this, EventArgs.Empty); + } + } + + public bool CursorVisible + { + get { return CG.CursorIsVisible(); } + set + { + if (value) + { + CG.DisplayShowCursor(IntPtr.Zero); + CG.AssociateMouseAndMouseCursorPosition(true); + } + else + { + CG.DisplayHideCursor(IntPtr.Zero); + ResetMouseToWindowCenter(); + CG.AssociateMouseAndMouseCursorPosition(false); + } } } public void Close() { - CancelEventArgs e = new CancelEventArgs(); - OnClosing(e); - - if (e.Cancel) - return; - - OnClosed(); - - Dispose(); + CancelEventArgs e = new CancelEventArgs(); + OnClosing(e); + + if (e.Cancel) + return; + + OnClosed(); + + Dispose(); } public WindowState WindowState @@ -944,188 +928,173 @@ namespace OpenTK.Platform.MacOS { if (windowState == WindowState.Fullscreen) return WindowState.Fullscreen; - + if (Carbon.API.IsWindowCollapsed(window.WindowRef)) return WindowState.Minimized; - + if (Carbon.API.IsWindowInStandardState(window.WindowRef)) { return WindowState.Maximized; } - + return WindowState.Normal; } set { if (value == WindowState) return; - + Debug.Print("Switching window state from {0} to {1}", WindowState, value); - WindowState oldState = WindowState; - - windowState = value; - - if (oldState == WindowState.Fullscreen) + WindowState oldState = WindowState; + + windowState = value; + + if (oldState == WindowState.Fullscreen) { - window.GoWindowedHack = true; - - // when returning from full screen, wait until the context is updated - // to actually do the work. - return; + window.GoWindowedHack = true; + + // when returning from full screen, wait until the context is updated + // to actually do the work. + return; } - + if (oldState == WindowState.Minimized) { API.CollapseWindow(window.WindowRef, false); } - - SetCarbonWindowState(); + + SetCarbonWindowState(); } } - private void SetCarbonWindowState() - { - CarbonPoint idealSize; + private void SetCarbonWindowState() + { + CarbonPoint idealSize; + + switch (windowState) + { + case WindowState.Fullscreen: + window.GoFullScreenHack = true; + + break; + + case WindowState.Maximized: + // hack because mac os has no concept of maximized. Instead windows are "zoomed" + // meaning they are maximized up to their reported ideal size. So we report a + // large ideal size. + idealSize = new CarbonPoint(9000, 9000); + API.ZoomWindowIdeal(window.WindowRef, WindowPartCode.inZoomOut, ref idealSize); + break; + + case WindowState.Normal: + if (WindowState == WindowState.Maximized) + { + idealSize = new CarbonPoint(); + API.ZoomWindowIdeal(window.WindowRef, WindowPartCode.inZoomIn, ref idealSize); + } - switch (windowState) - { - case WindowState.Fullscreen: - window.GoFullScreenHack = true; + break; + + case WindowState.Minimized: + API.CollapseWindow(window.WindowRef, true); + + break; + } + + + WindowStateChanged(this, EventArgs.Empty); + LoadSize(); + Resize(this, EventArgs.Empty); + } - break; + public WindowBorder WindowBorder + { + get { return windowBorder; } + set + { + if (windowBorder == value) + return; + + windowBorder = value; + + if (windowBorder == WindowBorder.Resizable) + { + API.ChangeWindowAttributes(window.WindowRef, WindowAttributes.Resizable | WindowAttributes.FullZoom, WindowAttributes.NoAttributes); + } - case WindowState.Maximized: - // hack because mac os has no concept of maximized. Instead windows are "zoomed" - // meaning they are maximized up to their reported ideal size. So we report a - // large ideal size. - idealSize = new CarbonPoint(9000, 9000); - API.ZoomWindowIdeal(window.WindowRef, WindowPartCode.inZoomOut, ref idealSize); - break; + else if (windowBorder == WindowBorder.Fixed) + { + API.ChangeWindowAttributes(window.WindowRef, WindowAttributes.NoAttributes, WindowAttributes.Resizable | WindowAttributes.FullZoom); + } + + WindowBorderChanged(this, EventArgs.Empty); + } + } - case WindowState.Normal: - if (WindowState == WindowState.Maximized) - { - idealSize = new CarbonPoint(); - API.ZoomWindowIdeal(window.WindowRef, WindowPartCode.inZoomIn, ref idealSize); - } - break; + #region --- Event wrappers --- - case WindowState.Minimized: - API.CollapseWindow(window.WindowRef, true); - - break; - } + private void OnKeyPress(KeyPressEventArgs keyPressArgs) + { + KeyPress(this, keyPressArgs); + } - OnWindowStateChanged(); + private void OnWindowStateChanged() + { + WindowStateChanged(this, EventArgs.Empty); + } - OnResize(); - } + protected virtual void OnClosing(CancelEventArgs e) + { + Closing(this, e); + } - public WindowBorder WindowBorder - { - get - { - return windowBorder; - } - set - { - if (windowBorder == value) - return; - - windowBorder = value; - - if (windowBorder == WindowBorder.Resizable) - { - API.ChangeWindowAttributes(window.WindowRef, WindowAttributes.Resizable | WindowAttributes.FullZoom, - WindowAttributes.NoAttributes); - } - else if (windowBorder == WindowBorder.Fixed) - { - API.ChangeWindowAttributes(window.WindowRef, WindowAttributes.NoAttributes, - WindowAttributes.Resizable | WindowAttributes.FullZoom); - } - - if (WindowBorderChanged != null) - WindowBorderChanged(this, EventArgs.Empty); - } - } - - #region --- Event wrappers --- - - private void OnKeyPress(KeyPressEventArgs keyPressArgs) - { - if (KeyPress != null) - KeyPress(this, keyPressArgs); - } + protected virtual void OnClosed() + { + Closed(this, EventArgs.Empty); + } - private void OnWindowStateChanged() - { - if (WindowStateChanged != null) - WindowStateChanged(this, EventArgs.Empty); - } + private void OnMouseLeave() + { + MouseLeave(this, EventArgs.Empty); + } - protected virtual void OnClosing(CancelEventArgs e) - { - if (Closing != null) - Closing(this, e); - } + private void OnMouseEnter() + { + MouseEnter(this, EventArgs.Empty); + } - protected virtual void OnClosed() - { - if (Closed != null) - Closed(this, EventArgs.Empty); - } + private void OnActivate() + { + mIsActive = true; + FocusedChanged(this, EventArgs.Empty); + } + private void OnDeactivate() + { + mIsActive = false; + FocusedChanged(this, EventArgs.Empty); + } + #endregion - private void OnMouseLeave() - { - if (MouseLeave != null) - MouseLeave(this, EventArgs.Empty); - } - - private void OnMouseEnter() - { - if (MouseEnter != null) - MouseEnter(this, EventArgs.Empty); - } - - private void OnActivate() - { - mIsActive = true; - if (FocusedChanged != null) - FocusedChanged(this, EventArgs.Empty); - } - private void OnDeactivate() - { - mIsActive = false; - if (FocusedChanged != null) - FocusedChanged(this, EventArgs.Empty); - } - - #endregion - - public event EventHandler Idle; - public event EventHandler Load; - public event EventHandler Unload; - public event EventHandler Move; - public event EventHandler Resize; - public event EventHandler Closing; - public event EventHandler Closed; - public event EventHandler Disposed; - public event EventHandler IconChanged; - public event EventHandler TitleChanged; - public event EventHandler ClientSizeChanged; - public event EventHandler VisibleChanged; - public event EventHandler WindowInfoChanged; - public event EventHandler FocusedChanged; - public event EventHandler WindowBorderChanged; - public event EventHandler WindowStateChanged; - public event EventHandler KeyPress; - public event EventHandler MouseEnter; - public event EventHandler MouseLeave; - + public event EventHandler Move = delegate { }; + public event EventHandler Resize = delegate { }; + public event EventHandler Closing = delegate { }; + public event EventHandler Closed = delegate { }; + public event EventHandler Disposed = delegate { }; + public event EventHandler IconChanged = delegate { }; + public event EventHandler TitleChanged = delegate { }; + public event EventHandler VisibleChanged = delegate { }; + public event EventHandler FocusedChanged = delegate { }; + public event EventHandler WindowBorderChanged = delegate { }; + public event EventHandler WindowStateChanged = delegate { }; + public event EventHandler KeyDown = delegate { }; + public event EventHandler KeyPress = delegate { }; + public event EventHandler KeyUp = delegate { }; + public event EventHandler MouseEnter = delegate { }; + public event EventHandler MouseLeave = delegate { }; + #endregion } } diff --git a/Source/OpenTK/Platform/MacOS/CarbonInput.cs b/Source/OpenTK/Platform/MacOS/CarbonInput.cs index b8d08b51..1fd65a2c 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonInput.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonInput.cs @@ -1,3 +1,30 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 + using System; using System.Collections.Generic; using System.Diagnostics; @@ -7,7 +34,7 @@ namespace OpenTK.Platform.MacOS { using Input; - class CarbonInput : IInputDriver + class CarbonInput : IInputDriver, IInputDriver2 { List dummy_keyboard_list = new List(1); List dummy_mice_list = new List(1); @@ -62,5 +89,29 @@ namespace OpenTK.Platform.MacOS } #endregion + + public IMouseDriver2 MouseDriver + { + get + { + throw new NotImplementedException(); + } + } + + public IKeyboardDriver2 KeyboardDriver + { + get + { + throw new NotImplementedException(); + } + } + + public IGamePadDriver GamePadDriver + { + get + { + throw new NotImplementedException(); + } + } } } diff --git a/Source/OpenTK/Platform/MacOS/CarbonWindowInfo.cs b/Source/OpenTK/Platform/MacOS/CarbonWindowInfo.cs index 7f3eabdf..cd594a49 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonWindowInfo.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonWindowInfo.cs @@ -36,14 +36,14 @@ namespace OpenTK.Platform.MacOS /// /// Describes a Carbon window. /// - sealed class CarbonWindowInfo : IWindowInfo + sealed class CarbonWindowInfo : IWindowInfo { IntPtr windowRef; bool ownHandle = false; bool disposed = false; bool isControl = false; - bool goFullScreenHack = false; - bool goWindowedHack = false; + bool goFullScreenHack = false; + bool goWindowedHack = false; #region Constructors @@ -72,16 +72,16 @@ namespace OpenTK.Platform.MacOS get { return this.windowRef; } } - internal bool GoFullScreenHack - { - get { return goFullScreenHack; } - set { goFullScreenHack = value; } - } - internal bool GoWindowedHack - { - get { return goWindowedHack; } - set { goWindowedHack = value; } - } + internal bool GoFullScreenHack + { + get { return goFullScreenHack; } + set { goFullScreenHack = value; } + } + internal bool GoWindowedHack + { + get { return goWindowedHack; } + set { goWindowedHack = value; } + } /// @@ -96,8 +96,7 @@ namespace OpenTK.Platform.MacOS /// A System.String that represents the current window. public override string ToString() { - return String.Format("MacOS.CarbonWindowInfo: Handle {0}", - this.WindowRef); + return String.Format("MacOS.CarbonWindowInfo: Handle {0}", this.WindowRef); } #endregion @@ -113,19 +112,19 @@ namespace OpenTK.Platform.MacOS { if (disposed) return; - + if (disposing) { - + } - + if (ownHandle) { Debug.Print("Disposing window {0}.", windowRef); Carbon.API.DisposeWindow(this.windowRef); windowRef = IntPtr.Zero; } - + disposed = true; } @@ -133,7 +132,7 @@ namespace OpenTK.Platform.MacOS { Dispose(false); } - + #endregion } } diff --git a/Source/OpenTK/Platform/MacOS/EventInfo.cs b/Source/OpenTK/Platform/MacOS/EventInfo.cs index 87ea61dc..32c9d689 100644 --- a/Source/OpenTK/Platform/MacOS/EventInfo.cs +++ b/Source/OpenTK/Platform/MacOS/EventInfo.cs @@ -1,11 +1,31 @@ +#region License // -// -// xCSCarbon +// The Open Toolkit Library License // +// Copyright (c) 2006 - 2010 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 + // Created by Erik Ylvisaker on 3/17/08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// -// using System; using System.Collections.Generic; @@ -25,14 +45,17 @@ namespace OpenTK.Platform.MacOS.Carbon uint _eventKind; EventClass _eventClass; - public EventClass EventClass { get { return _eventClass; }} - + public EventClass EventClass + { + get { return _eventClass; } + } + public WindowEventKind WindowEventKind { get { if (EventClass == EventClass.Window) - return (WindowEventKind) _eventKind; + return (WindowEventKind)_eventKind; else throw new InvalidCastException("Event is not a Window event."); } @@ -42,7 +65,7 @@ namespace OpenTK.Platform.MacOS.Carbon get { if (EventClass == EventClass.Keyboard) - return (KeyboardEventKind) _eventKind; + return (KeyboardEventKind)_eventKind; else throw new InvalidCastException("Event is not a Keyboard event."); } @@ -52,7 +75,7 @@ namespace OpenTK.Platform.MacOS.Carbon get { if (EventClass == EventClass.Mouse) - return (MouseEventKind) _eventKind; + return (MouseEventKind)_eventKind; else throw new InvalidCastException("Event is not an Mouse event."); } @@ -62,7 +85,7 @@ namespace OpenTK.Platform.MacOS.Carbon get { if (EventClass == EventClass.Application) - return (AppEventKind) _eventKind; + return (AppEventKind)_eventKind; else throw new InvalidCastException("Event is not an Application event."); } @@ -71,18 +94,18 @@ namespace OpenTK.Platform.MacOS.Carbon public override string ToString() { - switch(EventClass) + switch (EventClass) { - case EventClass.Application: - return "Event: App " + AppEventKind.ToString(); - case EventClass.Keyboard: - return "Event: Keyboard " + KeyboardEventKind.ToString(); - case EventClass.Mouse: - return "Event: Mouse " + MouseEventKind.ToString(); - case EventClass.Window: - return "Event: Window " + WindowEventKind.ToString(); + case EventClass.Application: + return "Event: App " + AppEventKind.ToString(); + case EventClass.Keyboard: + return "Event: Keyboard " + KeyboardEventKind.ToString(); + case EventClass.Mouse: + return "Event: Mouse " + MouseEventKind.ToString(); + case EventClass.Window: + return "Event: Window " + WindowEventKind.ToString(); } - + return "Event: Unknown Class " + EventClass.ToString() + " kind: " + _eventKind.ToString(); } } diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs new file mode 100755 index 00000000..1a5759a7 --- /dev/null +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -0,0 +1,937 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using OpenTK.Input; + +namespace OpenTK.Platform.MacOS +{ + using Carbon; + using CFAllocatorRef = System.IntPtr; + using CFDictionaryRef = System.IntPtr; + using CFIndex = System.IntPtr; + using CFRunLoop = System.IntPtr; + using CFString = System.IntPtr; + using CFStringRef = System.IntPtr; // Here used interchangeably with the CFString + using CFTypeRef = System.IntPtr; + using IOHIDDeviceRef = System.IntPtr; + using IOHIDElementRef = System.IntPtr; + using IOHIDManagerRef = System.IntPtr; + using IOHIDValueRef = System.IntPtr; + using IOOptionBits = System.IntPtr; + using IOReturn = System.IntPtr; + + // 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 + { + #region Fields + + readonly IOHIDManagerRef hidmanager; + + readonly Dictionary MouseDevices = + new Dictionary(new IntPtrEqualityComparer()); + readonly Dictionary MouseIndexToDevice = + new Dictionary(); + readonly Dictionary KeyboardDevices = + new Dictionary(new IntPtrEqualityComparer()); + readonly Dictionary KeyboardIndexToDevice = + new Dictionary(); + + readonly CFRunLoop RunLoop = CF.CFRunLoopGetMain(); + readonly CFString InputLoopMode = CF.RunLoopModeDefault; + readonly CFDictionary DeviceTypes = new CFDictionary(); + + readonly NativeMethods.IOHIDDeviceCallback HandleDeviceAdded; + readonly NativeMethods.IOHIDDeviceCallback HandleDeviceRemoved; + readonly NativeMethods.IOHIDValueCallback HandleDeviceValueReceived; + + #endregion + + #region Constructors + + public HIDInput() + { + Debug.Print("Using HIDInput."); + + HandleDeviceAdded = DeviceAdded; + HandleDeviceRemoved = DeviceRemoved; + HandleDeviceValueReceived = DeviceValueReceived; + + hidmanager = CreateHIDManager(); + RegisterHIDCallbacks(hidmanager); + } + + #endregion + + #region Private Members + + IOHIDManagerRef CreateHIDManager() + { + return NativeMethods.IOHIDManagerCreate(IntPtr.Zero, IntPtr.Zero); + } + + // Registers callbacks for device addition and removal. These callbacks + // are called when we run the loop in CheckDevicesMode + void RegisterHIDCallbacks(IOHIDManagerRef hidmanager) + { + NativeMethods.IOHIDManagerRegisterDeviceMatchingCallback( + hidmanager, HandleDeviceAdded, IntPtr.Zero); + NativeMethods.IOHIDManagerRegisterDeviceRemovalCallback( + hidmanager, HandleDeviceRemoved, IntPtr.Zero); + NativeMethods.IOHIDManagerScheduleWithRunLoop(hidmanager, + RunLoop, InputLoopMode); + + NativeMethods.IOHIDManagerSetDeviceMatching(hidmanager, DeviceTypes.Ref); + NativeMethods.IOHIDManagerOpen(hidmanager, IOOptionBits.Zero); + + OpenTK.Platform.MacOS.Carbon.CF.CFRunLoopRunInMode(InputLoopMode, 0.0, true); + } + + void DeviceAdded(IntPtr context, IOReturn res, IntPtr sender, IOHIDDeviceRef device) + { + if (NativeMethods.IOHIDDeviceOpen(device, IOOptionBits.Zero) == IOReturn.Zero) + { + if (NativeMethods.IOHIDDeviceConformsTo(device, + HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse)) + { + if (!MouseDevices.ContainsKey(device)) + { + Debug.Print("Mouse device {0} discovered", device); + MouseState state = new MouseState(); + state.IsConnected = true; + MouseIndexToDevice.Add(MouseDevices.Count, device); + MouseDevices.Add(device, state); + } + else + { + Debug.Print("Mouse device {0} reconnected", device); + MouseState state = MouseDevices[device]; + state.IsConnected = true; + MouseDevices[device] = state; + } + } + + if (NativeMethods.IOHIDDeviceConformsTo(device, + HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard)) + { + if (!KeyboardDevices.ContainsKey(device)) + { + Debug.Print("Keyboard device {0} discovered", device); + KeyboardState state = new KeyboardState(); + state.IsConnected = true; + KeyboardIndexToDevice.Add(KeyboardDevices.Count, device); + KeyboardDevices.Add(device, state); + } + else + { + Debug.Print("Keyboard device {0} reconnected", device); + KeyboardState state = KeyboardDevices[device]; + state.IsConnected = true; + KeyboardDevices[device] = state; + } + } + + NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, + HandleDeviceValueReceived, IntPtr.Zero); + NativeMethods.IOHIDDeviceScheduleWithRunLoop(device, RunLoop, InputLoopMode); + } + } + + void DeviceRemoved(IntPtr context, IOReturn res, IntPtr sender, IOHIDDeviceRef device) + { + if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse) && + MouseDevices.ContainsKey(device)) + { + Debug.Print("Mouse device {0} disconnected", device); + + // Keep the device in case it comes back later on + MouseState state = MouseDevices[device]; + state.IsConnected = false; + MouseDevices[device] = state; + } + + if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard) && + KeyboardDevices.ContainsKey(device)) + { + Debug.Print("Keyboard device {0} disconnected", device); + + // Keep the device in case it comes back later on + KeyboardState state = KeyboardDevices[device]; + state.IsConnected = false; + KeyboardDevices[device] = state; + } + + NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, null, IntPtr.Zero); + NativeMethods.IOHIDDeviceUnscheduleWithRunLoop(device, RunLoop, InputLoopMode); + } + + void DeviceValueReceived(IntPtr context, IOReturn res, IntPtr sender, IOHIDValueRef val) + { + MouseState mouse; + KeyboardState keyboard; + if (MouseDevices.TryGetValue(sender, out mouse)) + { + MouseDevices[sender] = UpdateMouse(mouse, val); + } + else if (KeyboardDevices.TryGetValue(sender, out keyboard)) + { + KeyboardDevices[sender] = UpdateKeyboard(keyboard, val); + } + } + + static MouseState UpdateMouse(MouseState state, IOHIDValueRef val) + { + IOHIDElementRef elem = NativeMethods.IOHIDValueGetElement(val); + int v_int = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32(); + //double v_physical = NativeMethods.IOHIDValueGetScaledValue(val, IOHIDValueScaleType.Physical); + //double v_calbrated = NativeMethods.IOHIDValueGetScaledValue(val, IOHIDValueScaleType.Calibrated); + HIDPage page = NativeMethods.IOHIDElementGetUsagePage(elem); + int usage = NativeMethods.IOHIDElementGetUsage(elem); + + switch (page) + { + case HIDPage.GenericDesktop: + switch ((HIDUsageGD)usage) + { + case HIDUsageGD.X: + state.X += v_int; + break; + + case HIDUsageGD.Y: + state.Y += v_int; + break; + + case HIDUsageGD.Wheel: + state.WheelPrecise += v_int; + break; + } + break; + + case HIDPage.Button: + state[OpenTK.Input.MouseButton.Left + usage - 1] = v_int == 1; + break; + } + + return state; + } + + static KeyboardState UpdateKeyboard(KeyboardState state, IOHIDValueRef val) + { + IOHIDElementRef elem = NativeMethods.IOHIDValueGetElement(val); + int v_int = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32(); + HIDPage page = NativeMethods.IOHIDElementGetUsagePage(elem); + int usage = NativeMethods.IOHIDElementGetUsage(elem); + + switch (page) + { + case HIDPage.GenericDesktop: + case HIDPage.KeyboardOrKeypad: + int raw = (int)usage; + if (raw >= RawKeyMap.Length || raw < 0) + { + Debug.Print("[Warning] Key {0} not mapped.", raw); + return state; + } + Key key = RawKeyMap[raw]; + state[key] = v_int != 0; + break; + } + + return state; + } + + #endregion + + #region IInputDriver2 Members + + public IMouseDriver2 MouseDriver { get { return this; } } + public IKeyboardDriver2 KeyboardDriver { get { return this; } } + public IGamePadDriver GamePadDriver { get { throw new NotImplementedException(); } } + + #endregion + + #region IMouseDriver2 Members + + MouseState IMouseDriver2.GetState() + { + MouseState master = new MouseState(); + foreach (KeyValuePair item in MouseDevices) + { + master.MergeBits(item.Value); + } + + return master; + } + + MouseState IMouseDriver2.GetState(int index) + { + IntPtr device; + if (MouseIndexToDevice.TryGetValue(index, out device)) + { + return MouseDevices[device]; + } + + return new MouseState(); + } + + void IMouseDriver2.SetPosition(double x, double y) + { + CG.SetLocalEventsSuppressionInterval(0.0); + CG.WarpMouseCursorPosition(new Carbon.HIPoint(x, y)); + } + + #endregion + + #region IKeyboardDriver2 + + KeyboardState IKeyboardDriver2.GetState() + { + KeyboardState master = new KeyboardState(); + foreach (KeyValuePair item in KeyboardDevices) + { + master.MergeBits(item.Value); + } + + return master; + } + + KeyboardState IKeyboardDriver2.GetState(int index) + { + IntPtr device; + if (KeyboardIndexToDevice.TryGetValue(index, out device)) + { + return KeyboardDevices[device]; + } + + return new KeyboardState(); + } + + string IKeyboardDriver2.GetDeviceName(int index) + { + IntPtr device; + if (KeyboardIndexToDevice.TryGetValue(index, out device)) + { + IntPtr vendor_id = NativeMethods.IOHIDDeviceGetProperty(device, NativeMethods.IOHIDVendorIDKey); + IntPtr product_id = NativeMethods.IOHIDDeviceGetProperty(device, NativeMethods.IOHIDProductIDKey); + // Todo: find out the real vendor/product name from the relevant ids. + return String.Format("{0}:{1}", vendor_id, product_id); + } + return String.Empty; + } + + #endregion + + #region NativeMethods + + class NativeMethods + { + const string hid = "/System/Library/Frameworks/IOKit.framework/Versions/Current/IOKit"; + + public static readonly CFString IOHIDVendorIDKey = CF.CFSTR("VendorID"); + public static readonly CFString IOHIDVendorIDSourceKey = CF.CFSTR("VendorIDSource"); + public static readonly CFString IOHIDProductIDKey = CF.CFSTR("ProductID"); + public static readonly CFString IOHIDVersionNumberKey = CF.CFSTR("VersionNumber"); + public static readonly CFString IOHIDManufacturerKey = CF.CFSTR("Manufacturer"); + public static readonly CFString IOHIDProductKey = CF.CFSTR("Product"); + public static readonly CFString IOHIDDeviceUsageKey = CF.CFSTR("DeviceUsage"); + public static readonly CFString IOHIDDeviceUsagePageKey = CF.CFSTR("DeviceUsagePage"); + public static readonly CFString IOHIDDeviceUsagePairsKey = CF.CFSTR("DeviceUsagePairs"); + + [DllImport(hid)] + public static extern IOHIDManagerRef IOHIDManagerCreate( + CFAllocatorRef allocator, IOOptionBits options) ; + + // This routine will be called when a new (matching) device is connected. + [DllImport(hid)] + public static extern void IOHIDManagerRegisterDeviceMatchingCallback( + IOHIDManagerRef inIOHIDManagerRef, + IOHIDDeviceCallback inIOHIDDeviceCallback, + IntPtr inContext); + + // This routine will be called when a (matching) device is disconnected. + [DllImport(hid)] + public static extern void IOHIDManagerRegisterDeviceRemovalCallback( + IOHIDManagerRef inIOHIDManagerRef, + IOHIDDeviceCallback inIOHIDDeviceCallback, + IntPtr inContext); + + [DllImport(hid)] + public static extern void IOHIDManagerScheduleWithRunLoop( + IOHIDManagerRef inIOHIDManagerRef, + CFRunLoop inCFRunLoop, + CFString inCFRunLoopMode); + + [DllImport(hid)] + public static extern void IOHIDManagerSetDeviceMatching( + IOHIDManagerRef manager, + CFDictionaryRef matching) ; + + [DllImport(hid)] + public static extern IOReturn IOHIDManagerOpen( + IOHIDManagerRef manager, + IOOptionBits options) ; + + [DllImport(hid)] + public static extern IOReturn IOHIDDeviceOpen( + IOHIDDeviceRef manager, + IOOptionBits opts); + + [DllImport(hid)] + public static extern CFTypeRef IOHIDDeviceGetProperty( + IOHIDDeviceRef device, + CFStringRef key); + + [DllImport(hid)] + public static extern bool IOHIDDeviceConformsTo( + IOHIDDeviceRef inIOHIDDeviceRef, // IOHIDDeviceRef for the HID device + HIDPage inUsagePage, // the usage page to test conformance with + int inUsage); // the usage to test conformance with + + [DllImport(hid)] + public static extern void IOHIDDeviceRegisterInputValueCallback( + IOHIDDeviceRef device, + IOHIDValueCallback callback, + IntPtr context); + + [DllImport(hid)] + public static extern void IOHIDDeviceScheduleWithRunLoop( + IOHIDDeviceRef device, + CFRunLoop inCFRunLoop, + CFString inCFRunLoopMode); + + [DllImport(hid)] + public static extern void IOHIDDeviceUnscheduleWithRunLoop( + IOHIDDeviceRef device, + CFRunLoop inCFRunLoop, + CFString inCFRunLoopMode); + + [DllImport(hid)] + public static extern IOHIDElementRef IOHIDValueGetElement(IOHIDValueRef @value); + + [DllImport(hid)] + public static extern CFIndex IOHIDValueGetIntegerValue(IOHIDValueRef @value); + + [DllImport(hid)] + public static extern double IOHIDValueGetScaledValue( + IOHIDValueRef @value, + IOHIDValueScaleType type) ; + + [DllImport(hid)] + public static extern int IOHIDElementGetUsage(IOHIDElementRef elem); + + [DllImport(hid)] + public static extern HIDPage IOHIDElementGetUsagePage(IOHIDElementRef elem); + + 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 IOHIDValueScaleType + { + Physical, // [device min, device max] + Calibrated // [-1, +1] + } + + enum HIDPage + { + Undefined = 0x00, + GenericDesktop = 0x01, + Simulation = 0x02, + VR = 0x03, + Sport = 0x04, + Game = 0x05, + /* Reserved 0x06 */ + KeyboardOrKeypad = 0x07, /* USB Device Class Definition for Human Interface Devices (HID). Note: the usage type for all key codes is Selector (Sel). */ + LEDs = 0x08, + Button = 0x09, + Ordinal = 0x0A, + Telephony = 0x0B, + Consumer = 0x0C, + Digitizer = 0x0D, + /* Reserved 0x0E */ + PID = 0x0F, /* USB Physical Interface Device definitions for force feedback and related devices. */ + Unicode = 0x10, + /* Reserved 0x11 - 0x13 */ + AlphanumericDisplay = 0x14, + /* Reserved 0x15 - 0x7F */ + /* Monitor 0x80 - 0x83 USB Device Class Definition for Monitor Devices */ + /* Power 0x84 - 0x87 USB Device Class Definition for Power Devices */ + PowerDevice = 0x84, /* Power Device Page */ + BatterySystem = 0x85, /* Battery System Page */ + /* Reserved 0x88 - 0x8B */ + BarCodeScanner = 0x8C, /* (Point of Sale) USB Device Class Definition for Bar Code Scanner Devices */ + WeighingDevice = 0x8D, /* (Point of Sale) USB Device Class Definition for Weighing Devices */ + Scale = 0x8D, /* (Point of Sale) USB Device Class Definition for Scale Devices */ + MagneticStripeReader = 0x8E, + /* ReservedPointofSalepages 0x8F */ + CameraControl = 0x90, /* USB Device Class Definition for Image Class Devices */ + Arcade = 0x91, /* OAAF Definitions for arcade and coinop related Devices */ + /* Reserved 0x92 - 0xFEFF */ + /* VendorDefined 0xFF00 - 0xFFFF */ + VendorDefinedStart = 0xFF00 + } + + // Generic desktop usage + enum HIDUsageGD + { + Pointer = 0x01, /* Physical Collection */ + Mouse = 0x02, /* Application Collection */ + /* 0x03 Reserved */ + Joystick = 0x04, /* Application Collection */ + GamePad = 0x05, /* Application Collection */ + Keyboard = 0x06, /* Application Collection */ + Keypad = 0x07, /* Application Collection */ + MultiAxisController = 0x08, /* Application Collection */ + /* 0x09 - 0x2F Reserved */ + X = 0x30, /* Dynamic Value */ + Y = 0x31, /* Dynamic Value */ + Z = 0x32, /* Dynamic Value */ + Rx = 0x33, /* Dynamic Value */ + Ry = 0x34, /* Dynamic Value */ + Rz = 0x35, /* Dynamic Value */ + Slider = 0x36, /* Dynamic Value */ + Dial = 0x37, /* Dynamic Value */ + Wheel = 0x38, /* Dynamic Value */ + Hatswitch = 0x39, /* Dynamic Value */ + CountedBuffer = 0x3A, /* Logical Collection */ + ByteCount = 0x3B, /* Dynamic Value */ + MotionWakeup = 0x3C, /* One-Shot Control */ + Start = 0x3D, /* On/Off Control */ + Select = 0x3E, /* On/Off Control */ + /* 0x3F Reserved */ + Vx = 0x40, /* Dynamic Value */ + Vy = 0x41, /* Dynamic Value */ + Vz = 0x42, /* Dynamic Value */ + Vbrx = 0x43, /* Dynamic Value */ + Vbry = 0x44, /* Dynamic Value */ + Vbrz = 0x45, /* Dynamic Value */ + Vno = 0x46, /* Dynamic Value */ + /* 0x47 - 0x7F Reserved */ + SystemControl = 0x80, /* Application Collection */ + SystemPowerDown = 0x81, /* One-Shot Control */ + SystemSleep = 0x82, /* One-Shot Control */ + SystemWakeUp = 0x83, /* One-Shot Control */ + SystemContextMenu = 0x84, /* One-Shot Control */ + SystemMainMenu = 0x85, /* One-Shot Control */ + SystemAppMenu = 0x86, /* One-Shot Control */ + SystemMenuHelp = 0x87, /* One-Shot Control */ + SystemMenuExit = 0x88, /* One-Shot Control */ + SystemMenu = 0x89, /* Selector */ + SystemMenuRight = 0x8A, /* Re-Trigger Control */ + SystemMenuLeft = 0x8B, /* Re-Trigger Control */ + SystemMenuUp = 0x8C, /* Re-Trigger Control */ + SystemMenuDown = 0x8D, /* Re-Trigger Control */ + /* 0x8E - 0x8F Reserved */ + DPadUp = 0x90, /* On/Off Control */ + DPadDown = 0x91, /* On/Off Control */ + DPadRight = 0x92, /* On/Off Control */ + DPadLeft = 0x93, /* On/Off Control */ + /* 0x94 - 0xFFFF Reserved */ + Reserved = 0xFFFF + } + + enum HIDButton + { + Button_1 = 0x01, /* (primary/trigger) */ + Button_2 = 0x02, /* (secondary) */ + Button_3 = 0x03, /* (tertiary) */ + Button_4 = 0x04, /* 4th button */ + /* ... */ + Button_65535 = 0xFFFF + } + + enum HIDKey + { + ErrorRollOver = 0x01, /* ErrorRollOver */ + POSTFail = 0x02, /* POSTFail */ + ErrorUndefined = 0x03, /* ErrorUndefined */ + A = 0x04, /* a or A */ + B = 0x05, /* b or B */ + C = 0x06, /* c or C */ + D = 0x07, /* d or D */ + E = 0x08, /* e or E */ + F = 0x09, /* f or F */ + G = 0x0A, /* g or G */ + H = 0x0B, /* h or H */ + I = 0x0C, /* i or I */ + J = 0x0D, /* j or J */ + K = 0x0E, /* k or K */ + L = 0x0F, /* l or L */ + M = 0x10, /* m or M */ + N = 0x11, /* n or N */ + O = 0x12, /* o or O */ + P = 0x13, /* p or P */ + Q = 0x14, /* q or Q */ + R = 0x15, /* r or R */ + S = 0x16, /* s or S */ + T = 0x17, /* t or T */ + U = 0x18, /* u or U */ + V = 0x19, /* v or V */ + W = 0x1A, /* w or W */ + X = 0x1B, /* x or X */ + Y = 0x1C, /* y or Y */ + Z = 0x1D, /* z or Z */ + Number1 = 0x1E, /* 1 or ! */ + Number2 = 0x1F, /* 2 or @ */ + Number3 = 0x20, /* 3 or # */ + Number4 = 0x21, /* 4 or $ */ + Number5 = 0x22, /* 5 or % */ + Number6 = 0x23, /* 6 or ^ */ + Number7 = 0x24, /* 7 or & */ + Number8 = 0x25, /* 8 or * */ + Number9 = 0x26, /* 9 or ( */ + Number0 = 0x27, /* 0 or ) */ + ReturnOrEnter = 0x28, /* Return (Enter) */ + Escape = 0x29, /* Escape */ + DeleteOrBackspace = 0x2A, /* Delete (Backspace) */ + Tab = 0x2B, /* Tab */ + Spacebar = 0x2C, /* Spacebar */ + Hyphen = 0x2D, /* - or _ */ + EqualSign = 0x2E, /* = or + */ + OpenBracket = 0x2F, /* [ or { */ + CloseBracket = 0x30, /* ] or } */ + Backslash = 0x31, /* \ or | */ + NonUSPound = 0x32, /* Non-US # or _ */ + Semicolon = 0x33, /* ; or : */ + Quote = 0x34, /* ' or " */ + GraveAccentAndTilde = 0x35, /* Grave Accent and Tilde */ + Comma = 0x36, /* , or < */ + Period = 0x37, /* . or > */ + Slash = 0x38, /* / or ? */ + CapsLock = 0x39, /* Caps Lock */ + F1 = 0x3A, /* F1 */ + F2 = 0x3B, /* F2 */ + F3 = 0x3C, /* F3 */ + F4 = 0x3D, /* F4 */ + F5 = 0x3E, /* F5 */ + F6 = 0x3F, /* F6 */ + F7 = 0x40, /* F7 */ + F8 = 0x41, /* F8 */ + F9 = 0x42, /* F9 */ + F10 = 0x43, /* F10 */ + F11 = 0x44, /* F11 */ + F12 = 0x45, /* F12 */ + PrintScreen = 0x46, /* Print Screen */ + ScrollLock = 0x47, /* Scroll Lock */ + Pause = 0x48, /* Pause */ + Insert = 0x49, /* Insert */ + Home = 0x4A, /* Home */ + PageUp = 0x4B, /* Page Up */ + DeleteForward = 0x4C, /* Delete Forward */ + End = 0x4D, /* End */ + PageDown = 0x4E, /* Page Down */ + RightArrow = 0x4F, /* Right Arrow */ + LeftArrow = 0x50, /* Left Arrow */ + DownArrow = 0x51, /* Down Arrow */ + UpArrow = 0x52, /* Up Arrow */ + KeypadNumLock = 0x53, /* Keypad NumLock or Clear */ + KeypadSlash = 0x54, /* Keypad / */ + KeypadAsterisk = 0x55, /* Keypad * */ + KeypadHyphen = 0x56, /* Keypad - */ + KeypadPlus = 0x57, /* Keypad + */ + KeypadEnter = 0x58, /* Keypad Enter */ + Keypad1 = 0x59, /* Keypad 1 or End */ + Keypad2 = 0x5A, /* Keypad 2 or Down Arrow */ + Keypad3 = 0x5B, /* Keypad 3 or Page Down */ + Keypad4 = 0x5C, /* Keypad 4 or Left Arrow */ + Keypad5 = 0x5D, /* Keypad 5 */ + Keypad6 = 0x5E, /* Keypad 6 or Right Arrow */ + Keypad7 = 0x5F, /* Keypad 7 or Home */ + Keypad8 = 0x60, /* Keypad 8 or Up Arrow */ + Keypad9 = 0x61, /* Keypad 9 or Page Up */ + Keypad0 = 0x62, /* Keypad 0 or Insert */ + KeypadPeriod = 0x63, /* Keypad . or Delete */ + NonUSBackslash = 0x64, /* Non-US \ or | */ + Application = 0x65, /* Application */ + Power = 0x66, /* Power */ + KeypadEqualSign = 0x67, /* Keypad = */ + F13 = 0x68, /* F13 */ + F14 = 0x69, /* F14 */ + F15 = 0x6A, /* F15 */ + F16 = 0x6B, /* F16 */ + F17 = 0x6C, /* F17 */ + F18 = 0x6D, /* F18 */ + F19 = 0x6E, /* F19 */ + F20 = 0x6F, /* F20 */ + F21 = 0x70, /* F21 */ + F22 = 0x71, /* F22 */ + F23 = 0x72, /* F23 */ + F24 = 0x73, /* F24 */ + Execute = 0x74, /* Execute */ + Help = 0x75, /* Help */ + Menu = 0x76, /* Menu */ + Select = 0x77, /* Select */ + Stop = 0x78, /* Stop */ + Again = 0x79, /* Again */ + Undo = 0x7A, /* Undo */ + Cut = 0x7B, /* Cut */ + Copy = 0x7C, /* Copy */ + Paste = 0x7D, /* Paste */ + Find = 0x7E, /* Find */ + Mute = 0x7F, /* Mute */ + VolumeUp = 0x80, /* Volume Up */ + VolumeDown = 0x81, /* Volume Down */ + LockingCapsLock = 0x82, /* Locking Caps Lock */ + LockingNumLock = 0x83, /* Locking Num Lock */ + LockingScrollLock = 0x84, /* Locking Scroll Lock */ + KeypadComma = 0x85, /* Keypad Comma */ + KeypadEqualSignAS400 = 0x86, /* Keypad Equal Sign for AS/400 */ + International1 = 0x87, /* International1 */ + International2 = 0x88, /* International2 */ + International3 = 0x89, /* International3 */ + International4 = 0x8A, /* International4 */ + International5 = 0x8B, /* International5 */ + International6 = 0x8C, /* International6 */ + International7 = 0x8D, /* International7 */ + International8 = 0x8E, /* International8 */ + International9 = 0x8F, /* International9 */ + LANG1 = 0x90, /* LANG1 */ + LANG2 = 0x91, /* LANG2 */ + LANG3 = 0x92, /* LANG3 */ + LANG4 = 0x93, /* LANG4 */ + LANG5 = 0x94, /* LANG5 */ + LANG6 = 0x95, /* LANG6 */ + LANG7 = 0x96, /* LANG7 */ + LANG8 = 0x97, /* LANG8 */ + LANG9 = 0x98, /* LANG9 */ + AlternateErase = 0x99, /* AlternateErase */ + SysReqOrAttention = 0x9A, /* SysReq/Attention */ + Cancel = 0x9B, /* Cancel */ + Clear = 0x9C, /* Clear */ + Prior = 0x9D, /* Prior */ + Return = 0x9E, /* Return */ + Separator = 0x9F, /* Separator */ + Out = 0xA0, /* Out */ + Oper = 0xA1, /* Oper */ + ClearOrAgain = 0xA2, /* Clear/Again */ + CrSelOrProps = 0xA3, /* CrSel/Props */ + ExSel = 0xA4, /* ExSel */ + /* 0xA5-0xDF Reserved */ + LeftControl = 0xE0, /* Left Control */ + LeftShift = 0xE1, /* Left Shift */ + LeftAlt = 0xE2, /* Left Alt */ + LeftGUI = 0xE3, /* Left GUI */ + RightControl = 0xE4, /* Right Control */ + RightShift = 0xE5, /* Right Shift */ + RightAlt = 0xE6, /* Right Alt */ + RightGUI = 0xE7, /* Right GUI */ + /* 0xE8-0xFFFF Reserved */ + //_Reserved = 0xFFFF + } + + // Maps HIDKey to OpenTK.Input.Key. + static readonly Key[] RawKeyMap = new Key[] + { + Key.Unknown, + Key.Unknown, /* ErrorRollOver */ + Key.Unknown, /* POSTFail */ + Key.Unknown, /* ErrorUndefined */ + Key.A, /* a or A */ + Key.B, /* b or B */ + Key.C, /* c or C */ + Key.D, /* d or D */ + Key.E, /* e or E */ + Key.F, /* f or F */ + Key.G, /* g or G */ + Key.H, /* h or H */ + Key.I, /* i or I */ + Key.J, /* j or J */ + Key.K, /* k or K */ + Key.L, /* l or L */ + Key.M, /* m or M */ + Key.N, /* n or N */ + Key.O, /* o or O */ + Key.P, /* p or P */ + Key.Q, /* q or Q */ + Key.R, /* r or R */ + Key.S, /* s or S */ + Key.T, /* t or T */ + Key.U, /* u or U */ + Key.V, /* v or V */ + Key.W, /* w or W */ + Key.X, /* x or X */ + Key.Y, /* y or Y */ + Key.Z, /* z or Z */ + Key.Number1, /* 1 or ! */ + Key.Number2, /* 2 or @ */ + Key.Number3, /* 3 or # */ + Key.Number4, /* 4 or $ */ + Key.Number5, /* 5 or % */ + Key.Number6, /* 6 or ^ */ + Key.Number7, /* 7 or & */ + Key.Number8, /* 8 or * */ + Key.Number9, /* 9 or ( */ + Key.Number0, /* 0 or ) */ + Key.Enter, /* Return (Enter) */ + Key.Escape, /* Escape */ + Key.BackSpace, /* Delete (Backspace) */ + Key.Tab, /* Tab */ + Key.Space, /* Spacebar */ + Key.Minus, /* - or _ */ + Key.Plus, /* = or + */ + Key.BracketLeft, /* [ or { */ + Key.BracketRight, /* ] or } */ + Key.BackSlash, /* \ or | */ + Key.Minus, /* Non-US # or _ */ + Key.Semicolon, /* ; or : */ + Key.Quote, /* ' or " */ + Key.Tilde, /* Grave Accent and Tilde */ + Key.Comma, /* , or < */ + Key.Period, /* . or > */ + Key.Slash, /* / or ? */ + Key.CapsLock, /* Caps Lock */ + Key.F1, /* F1 */ + Key.F2, /* F2 */ + Key.F3, /* F3 */ + Key.F4, /* F4 */ + Key.F5, /* F5 */ + Key.F6, /* F6 */ + Key.F7, /* F7 */ + Key.F8, /* F8 */ + Key.F9, /* F9 */ + Key.F10, /* F10 */ + Key.F11, /* F11 */ + Key.F12, /* F12 */ + Key.PrintScreen, /* Print Screen */ + Key.ScrollLock, /* Scroll Lock */ + Key.Pause, /* Pause */ + Key.Insert, /* Insert */ + Key.Home, /* Home */ + Key.PageUp, /* Page Up */ + Key.Delete, /* Delete Forward */ + Key.End, /* End */ + Key.PageDown, /* Page Down */ + Key.Right, /* Right Arrow */ + Key.Left, /* Left Arrow */ + Key.Down, /* Down Arrow */ + Key.Up, /* Up Arrow */ + Key.NumLock, /* Keypad NumLock or Clear */ + Key.KeypadDivide, /* Keypad / */ + Key.KeypadMultiply, /* Keypad * */ + Key.KeypadMinus, /* Keypad - */ + Key.KeypadPlus, /* Keypad + */ + Key.KeypadEnter, /* Keypad Enter */ + Key.Keypad1, /* Keypad 1 or End */ + Key.Keypad2, /* Keypad 2 or Down Arrow */ + Key.Keypad3, /* Keypad 3 or Page Down */ + Key.Keypad4, /* Keypad 4 or Left Arrow */ + Key.Keypad5, /* Keypad 5 */ + Key.Keypad6, /* Keypad 6 or Right Arrow */ + Key.Keypad7, /* Keypad 7 or Home */ + Key.Keypad8, /* Keypad 8 or Up Arrow */ + Key.Keypad9, /* Keypad 9 or Page Up */ + Key.Keypad0, /* Keypad 0 or Insert */ + Key.KeypadDecimal, /* Keypad . or Delete */ + Key.BackSlash, /* Non-US \ or | */ + Key.Unknown, /* Application */ + Key.Unknown, /* Power */ + Key.Unknown, /* Keypad = */ + Key.F13, /* F13 */ + Key.F14, /* F14 */ + Key.F15, /* F15 */ + Key.F16, /* F16 */ + Key.F17, /* F17 */ + Key.F18, /* F18 */ + Key.F19, /* F19 */ + Key.F20, /* F20 */ + Key.F21, /* F21 */ + Key.F22, /* F22 */ + Key.F23, /* F23 */ + Key.F24, /* F24 */ + Key.Unknown, /* Execute */ + Key.Unknown, /* Help */ + Key.Menu, /* Menu */ + Key.Unknown, /* Select */ + Key.Unknown, /* Stop */ + Key.Unknown, /* Again */ + Key.Unknown, /* Undo */ + Key.Unknown, /* Cut */ + Key.Unknown, /* Copy */ + Key.Unknown, /* Paste */ + Key.Unknown, /* Find */ + Key.Unknown, /* Mute */ + Key.Unknown, /* Volume Up */ + Key.Unknown, /* Volume Down */ + Key.CapsLock, /* Locking Caps Lock */ + Key.NumLock , /* Locking Num Lock */ + Key.ScrollLock, /* Locking Scroll Lock */ + Key.KeypadDecimal, /* Keypad Comma */ + Key.Unknown, /* Keypad Equal Sign for AS/400 */ + Key.Unknown, /* International1 */ + Key.Unknown, /* International2 */ + Key.Unknown, /* International3 */ + Key.Unknown, /* International4 */ + Key.Unknown, /* International5 */ + Key.Unknown, /* International6 */ + Key.Unknown, /* International7 */ + Key.Unknown, /* International8 */ + Key.Unknown, /* International9 */ + Key.Unknown, /* LANG1 */ + Key.Unknown, /* LANG2 */ + Key.Unknown, /* LANG3 */ + Key.Unknown, /* LANG4 */ + Key.Unknown, /* LANG5 */ + Key.Unknown, /* LANG6 */ + Key.Unknown, /* LANG7 */ + Key.Unknown, /* LANG8 */ + Key.Unknown, /* LANG9 */ + Key.Unknown, /* AlternateErase */ + Key.Unknown, /* SysReq/Attention */ + Key.Unknown, /* Cancel */ + Key.Unknown, /* Clear */ + Key.Unknown, /* Prior */ + Key.Enter, /* Return */ + Key.Unknown, /* Separator */ + Key.Unknown, /* Out */ + Key.Unknown, /* Oper */ + Key.Unknown, /* Clear/Again */ + Key.Unknown, /* CrSel/Props */ + Key.Unknown, /* ExSel */ + /* 0xA5-0xDF Reserved */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Key.LControl, /* Left Control */ + Key.LShift, /* Left Shift */ + Key.LAlt, /* Left Alt */ + Key.LWin, /* Left GUI */ + Key.RControl, /* Right Control */ + Key.RShift, /* Right Shift */ + Key.RAlt, /* Right Alt */ + Key.RWin, /* Right GUI */ + /* 0xE8-0xFFFF Reserved */ + }; + + #endregion + } +} + diff --git a/Source/OpenTK/Platform/MacOS/MacOSException.cs b/Source/OpenTK/Platform/MacOS/MacOSException.cs index a8082347..47489398 100644 --- a/Source/OpenTK/Platform/MacOS/MacOSException.cs +++ b/Source/OpenTK/Platform/MacOS/MacOSException.cs @@ -1,3 +1,31 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 + +// Created by Erik Ylvisaker on 3/17/08. using System; diff --git a/Source/OpenTK/Platform/MacOS/MacOSFactory.cs b/Source/OpenTK/Platform/MacOS/MacOSFactory.cs index 00282dd1..066e2879 100644 --- a/Source/OpenTK/Platform/MacOS/MacOSFactory.cs +++ b/Source/OpenTK/Platform/MacOS/MacOSFactory.cs @@ -28,6 +28,7 @@ using System; using System.Collections.Generic; using System.Text; +using OpenTK.Input; namespace OpenTK.Platform.MacOS { @@ -35,6 +36,12 @@ namespace OpenTK.Platform.MacOS class MacOSFactory : IPlatformFactory { + #region Fields + + readonly IInputDriver2 InputDriver = new HIDInput(); + + #endregion + #region IPlatformFactory Members public virtual INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) @@ -55,7 +62,7 @@ namespace OpenTK.Platform.MacOS public virtual IGraphicsContext CreateGLContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) { return new AglContext(handle, window, shareContext); - } + } public virtual GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext() { @@ -70,9 +77,14 @@ namespace OpenTK.Platform.MacOS return new MacOSGraphicsMode(); } - public virtual OpenTK.Input.IKeyboardDriver CreateKeyboardDriver() + public virtual OpenTK.Input.IKeyboardDriver2 CreateKeyboardDriver() { - throw new NotImplementedException(); + return InputDriver.KeyboardDriver; + } + + public virtual OpenTK.Input.IMouseDriver2 CreateMouseDriver() + { + return InputDriver.MouseDriver; } #endregion diff --git a/Source/OpenTK/Platform/MacOS/MacOSGraphicsMode.cs b/Source/OpenTK/Platform/MacOS/MacOSGraphicsMode.cs index ef3590ce..28d49f2b 100644 --- a/Source/OpenTK/Platform/MacOS/MacOSGraphicsMode.cs +++ b/Source/OpenTK/Platform/MacOS/MacOSGraphicsMode.cs @@ -1,23 +1,155 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 + using System; using System.Collections.Generic; +using System.Diagnostics; using System.Text; +using OpenTK.Graphics; namespace OpenTK.Platform.MacOS { - using Graphics; + using Carbon; - class MacOSGraphicsMode : IGraphicsMode + class MacOSGraphicsMode : IGraphicsMode { #region IGraphicsMode Members - public GraphicsMode SelectGraphicsMode(ColorFormat color, int depth, int stencil, int samples, ColorFormat accum, int buffers, bool stereo) + public GraphicsMode SelectGraphicsMode(ColorFormat color, int depth, int stencil, + int samples, ColorFormat accum, int buffers, bool stereo) { - GraphicsMode gfx = new GraphicsMode((IntPtr)1, color, depth, stencil, samples, - accum, buffers, stereo); + IntPtr pixelformat = SelectPixelFormat(color, depth, stencil, samples, accum, buffers, stereo); + return GetGraphicsModeFromPixelFormat(pixelformat); + } - System.Diagnostics.Debug.Print("Created dummy graphics mode."); + #endregion - return gfx; + #region Private Members + + GraphicsMode GetGraphicsModeFromPixelFormat(IntPtr pixelformat) + { + int r, g, b, a; + Agl.aglDescribePixelFormat(pixelformat, Agl.PixelFormatAttribute.AGL_RED_SIZE, out r); + Agl.aglDescribePixelFormat(pixelformat, Agl.PixelFormatAttribute.AGL_GREEN_SIZE, out g); + Agl.aglDescribePixelFormat(pixelformat, Agl.PixelFormatAttribute.AGL_BLUE_SIZE, out b); + Agl.aglDescribePixelFormat(pixelformat, Agl.PixelFormatAttribute.AGL_ALPHA_SIZE, out a); + int ar, ag, ab, aa; + Agl.aglDescribePixelFormat(pixelformat, Agl.PixelFormatAttribute.AGL_ACCUM_ALPHA_SIZE, out aa); + Agl.aglDescribePixelFormat(pixelformat, Agl.PixelFormatAttribute.AGL_ACCUM_RED_SIZE, out ar); + Agl.aglDescribePixelFormat(pixelformat, Agl.PixelFormatAttribute.AGL_ACCUM_GREEN_SIZE, out ag); + Agl.aglDescribePixelFormat(pixelformat, Agl.PixelFormatAttribute.AGL_ACCUM_BLUE_SIZE, out ab); + int depth, stencil, samples, buffers, stereo; + Agl.aglDescribePixelFormat(pixelformat, Agl.PixelFormatAttribute.AGL_DEPTH_SIZE, out depth); + Agl.aglDescribePixelFormat(pixelformat, Agl.PixelFormatAttribute.AGL_STENCIL_SIZE, out stencil); + Agl.aglDescribePixelFormat(pixelformat, Agl.PixelFormatAttribute.AGL_SAMPLES_ARB, out samples); + Agl.aglDescribePixelFormat(pixelformat, Agl.PixelFormatAttribute.AGL_DOUBLEBUFFER, out buffers); + Agl.aglDescribePixelFormat(pixelformat, Agl.PixelFormatAttribute.AGL_STEREO, out stereo); + + return new GraphicsMode(pixelformat, new ColorFormat(r, g, b, a), + depth, stencil, samples, new ColorFormat(ar, ag, ab, aa), buffers + 1, stereo != 0); + } + + IntPtr SelectPixelFormat(ColorFormat color, int depth, int stencil, int samples, + ColorFormat accum, int buffers, bool stereo) + { + List attribs = new List(); + + Debug.Print("Bits per pixel: {0}", color.BitsPerPixel); + + if (color.BitsPerPixel > 0) + { + if (!color.IsIndexed) + { + attribs.Add((int)Agl.PixelFormatAttribute.AGL_RGBA); + } + attribs.Add((int)Agl.PixelFormatAttribute.AGL_RED_SIZE); + attribs.Add(color.Red); + attribs.Add((int)Agl.PixelFormatAttribute.AGL_GREEN_SIZE); + attribs.Add(color.Green); + attribs.Add((int)Agl.PixelFormatAttribute.AGL_BLUE_SIZE); + attribs.Add(color.Blue); + attribs.Add((int)Agl.PixelFormatAttribute.AGL_ALPHA_SIZE); + attribs.Add(color.Alpha); + } + + Debug.Print("Depth: {0}", depth); + + if (depth > 0) + { + attribs.Add((int)Agl.PixelFormatAttribute.AGL_DEPTH_SIZE); + attribs.Add(depth); + } + + if (buffers > 1) + { + attribs.Add((int)Agl.PixelFormatAttribute.AGL_DOUBLEBUFFER); + } + + if (stencil > 1) + { + attribs.Add((int)Agl.PixelFormatAttribute.AGL_STENCIL_SIZE); + attribs.Add(stencil); + } + + if (accum.BitsPerPixel > 0) + { + attribs.Add((int)Agl.PixelFormatAttribute.AGL_ACCUM_ALPHA_SIZE); + attribs.Add(accum.Alpha); + attribs.Add((int)Agl.PixelFormatAttribute.AGL_ACCUM_BLUE_SIZE); + attribs.Add(accum.Blue); + attribs.Add((int)Agl.PixelFormatAttribute.AGL_ACCUM_GREEN_SIZE); + attribs.Add(accum.Green); + attribs.Add((int)Agl.PixelFormatAttribute.AGL_ACCUM_RED_SIZE); + attribs.Add(accum.Red); + } + + if (samples > 0) + { + attribs.Add((int)Agl.PixelFormatAttribute.AGL_SAMPLE_BUFFERS_ARB); + attribs.Add(1); + attribs.Add((int)Agl.PixelFormatAttribute.AGL_SAMPLES_ARB); + attribs.Add(samples); + } + + if (stereo) + { + attribs.Add((int)Agl.PixelFormatAttribute.AGL_STEREO); + } + + attribs.Add(0); + attribs.Add(0); + + IntPtr pixelformat = Agl.aglChoosePixelFormat(IntPtr.Zero, 0, attribs.ToArray()); + if (pixelformat == IntPtr.Zero) + { + throw new GraphicsModeException(String.Format( + "[Error] Failed to select GraphicsMode, error {0}.", Agl.GetError())); + } + return pixelformat; } #endregion diff --git a/Source/OpenTK/Platform/MacOS/MacOSKeyMap.cs b/Source/OpenTK/Platform/MacOS/MacOSKeyMap.cs index a92f160f..49e86033 100644 --- a/Source/OpenTK/Platform/MacOS/MacOSKeyMap.cs +++ b/Source/OpenTK/Platform/MacOS/MacOSKeyMap.cs @@ -1,3 +1,32 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 + +// Created by Erik Ylvisaker on 3/17/08. + using System; using System.Collections.Generic; using System.Text; @@ -12,7 +41,7 @@ namespace OpenTK.Platform.MacOS public MacOSKeyMap() { // comments indicate members of the Key enum that are missing - + Add(MacOSKeyCode.A, Key.A); // AltLeft // AltRight @@ -127,7 +156,7 @@ namespace OpenTK.Platform.MacOS Add(MacOSKeyCode.X, Key.X); Add(MacOSKeyCode.Y, Key.Y); Add(MacOSKeyCode.Z, Key.Z); - + } } } diff --git a/Source/OpenTK/Platform/MacOS/QuartzDisplayDeviceDriver.cs b/Source/OpenTK/Platform/MacOS/QuartzDisplayDeviceDriver.cs index 3f3e65af..b7da1bc1 100644 --- a/Source/OpenTK/Platform/MacOS/QuartzDisplayDeviceDriver.cs +++ b/Source/OpenTK/Platform/MacOS/QuartzDisplayDeviceDriver.cs @@ -1,23 +1,43 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 + using System; using System.Collections.Generic; using System.Diagnostics; +using System.Drawing; +using OpenTK.Platform.MacOS.Carbon; namespace OpenTK.Platform.MacOS { - using System.Drawing; - using Carbon; - - class QuartzDisplayDeviceDriver : IDisplayDeviceDriver + sealed class QuartzDisplayDeviceDriver : DisplayDeviceBase { static object display_lock = new object(); - static Dictionary displayMap = - new Dictionary(); - - static IntPtr mainDisplay; - internal static IntPtr MainDisplay { get { return mainDisplay; } } - - static QuartzDisplayDeviceDriver() + public QuartzDisplayDeviceDriver() { lock (display_lock) { @@ -30,158 +50,149 @@ namespace OpenTK.Platform.MacOS const int maxDisplayCount = 20; IntPtr[] displays = new IntPtr[maxDisplayCount]; int displayCount; - + unsafe { - fixed(IntPtr* displayPtr = displays) + fixed (IntPtr* displayPtr = displays) { CG.GetActiveDisplayList(maxDisplayCount, displayPtr, out displayCount); } } - + Debug.Print("CoreGraphics reported {0} display(s).", displayCount); Debug.Indent(); - + for (int i = 0; i < displayCount; i++) { IntPtr currentDisplay = displays[i]; - + // according to docs, first element in the array is always the // main display. bool primary = (i == 0); - - if (primary) - mainDisplay = currentDisplay; - + // gets current settings int currentWidth = CG.DisplayPixelsWide(currentDisplay); int currentHeight = CG.DisplayPixelsHigh(currentDisplay); Debug.Print("Display {0} is at {1}x{2}", i, currentWidth, currentHeight); - - IntPtr displayModesPtr = CG.DisplayAvailableModes(currentDisplay); + + IntPtr displayModesPtr = CG.DisplayAvailableModes(currentDisplay); CFArray displayModes = new CFArray(displayModesPtr); Debug.Print("Supports {0} display modes.", displayModes.Count); - + DisplayResolution opentk_dev_current_res = null; List opentk_dev_available_res = new List(); IntPtr currentModePtr = CG.DisplayCurrentMode(currentDisplay); CFDictionary currentMode = new CFDictionary(currentModePtr); - + for (int j = 0; j < displayModes.Count; j++) { CFDictionary dict = new CFDictionary(displayModes[j]); - int width = (int) dict.GetNumberValue("Width"); - int height = (int) dict.GetNumberValue("Height"); - int bpp = (int) dict.GetNumberValue("BitsPerPixel"); + int width = (int)dict.GetNumberValue("Width"); + int height = (int)dict.GetNumberValue("Height"); + int bpp = (int)dict.GetNumberValue("BitsPerPixel"); double freq = dict.GetNumberValue("RefreshRate"); bool current = currentMode.Ref == dict.Ref; - + //if (current) Debug.Write(" * "); //else Debug.Write(" "); - + //Debug.Print("Mode {0} is {1}x{2}x{3} @ {4}.", j, width, height, bpp, freq); - + DisplayResolution thisRes = new DisplayResolution(0, 0, width, height, bpp, (float)freq); opentk_dev_available_res.Add(thisRes); - + if (current) opentk_dev_current_res = thisRes; - + } + + HIRect bounds = CG.DisplayBounds(currentDisplay); + Rectangle newRect = new Rectangle((int)bounds.Origin.X, (int)bounds.Origin.Y, (int)bounds.Size.Width, (int)bounds.Size.Height); + + Debug.Print("Display {0} bounds: {1}", i, newRect); + + DisplayDevice opentk_dev = new DisplayDevice(opentk_dev_current_res, + primary, opentk_dev_available_res, newRect, currentDisplay); - HIRect bounds = CG.DisplayBounds(currentDisplay); - Rectangle newRect = new Rectangle( - (int)bounds.Origin.X, (int)bounds.Origin.Y, (int)bounds.Size.Width, (int)bounds.Size.Height); + AvailableDevices.Add(opentk_dev); - Debug.Print("Display {0} bounds: {1}", i, newRect); - - DisplayDevice opentk_dev = - new DisplayDevice(opentk_dev_current_res, primary, opentk_dev_available_res, newRect); - - displayMap.Add(opentk_dev, currentDisplay); + if (primary) + Primary = opentk_dev; } - + Debug.Unindent(); } } - - internal static IntPtr HandleTo(DisplayDevice displayDevice) - { - if (displayMap.ContainsKey(displayDevice)) - return displayMap[displayDevice]; - else - return IntPtr.Zero; - } + static internal IntPtr HandleTo(DisplayDevice displayDevice) + { + return (IntPtr)displayDevice.Id; + } #region IDisplayDeviceDriver Members Dictionary storedModes = new Dictionary(); List displaysCaptured = new List(); - - public bool TryChangeResolution(DisplayDevice device, DisplayResolution resolution) - { - IntPtr display = displayMap[device]; - IntPtr currentModePtr = CG.DisplayCurrentMode(display); + public sealed override bool TryChangeResolution(DisplayDevice device, DisplayResolution resolution) + { + IntPtr display = HandleTo(device); + IntPtr currentModePtr = CG.DisplayCurrentMode(display); + if (storedModes.ContainsKey(display) == false) { - storedModes.Add(display, currentModePtr); + storedModes.Add(display, currentModePtr); } - + IntPtr displayModesPtr = CG.DisplayAvailableModes(display); CFArray displayModes = new CFArray(displayModesPtr); - + for (int j = 0; j < displayModes.Count; j++) { CFDictionary dict = new CFDictionary(displayModes[j]); - + int width = (int)dict.GetNumberValue("Width"); int height = (int)dict.GetNumberValue("Height"); int bpp = (int)dict.GetNumberValue("BitsPerPixel"); double freq = dict.GetNumberValue("RefreshRate"); - - if (width == resolution.Width && - height == resolution.Height && - bpp == resolution.BitsPerPixel && - System.Math.Abs(freq - resolution.RefreshRate) < 1e-6) + + if (width == resolution.Width && height == resolution.Height && bpp == resolution.BitsPerPixel && System.Math.Abs(freq - resolution.RefreshRate) < 1e-6) { if (displaysCaptured.Contains(display) == false) { CG.DisplayCapture(display); } - + Debug.Print("Changing resolution to {0}x{1}x{2}@{3}.", width, height, bpp, freq); - + CG.DisplaySwitchToMode(display, displayModes[j]); - + return true; } - + } return false; } - public bool TryRestoreResolution(DisplayDevice device) + public sealed override bool TryRestoreResolution(DisplayDevice device) { - IntPtr display = displayMap[device]; - + IntPtr display = HandleTo(device); + if (storedModes.ContainsKey(display)) { Debug.Print("Restoring resolution."); - + CG.DisplaySwitchToMode(display, storedModes[display]); CG.DisplayRelease(display); displaysCaptured.Remove(display); - + return true; } - + return false; } #endregion - - } + } } diff --git a/Source/OpenTK/Platform/Utilities.cs b/Source/OpenTK/Platform/Utilities.cs index ae44a2f4..4dd5aeb7 100644 --- a/Source/OpenTK/Platform/Utilities.cs +++ b/Source/OpenTK/Platform/Utilities.cs @@ -9,7 +9,6 @@ using System; using System.Collections.Generic; using System.Text; -using System.Windows.Forms; using System.Runtime.InteropServices; using System.Reflection; using System.Diagnostics; diff --git a/Source/OpenTK/Platform/Windows/API.cs b/Source/OpenTK/Platform/Windows/API.cs index 920d081b..97b4115b 100644 --- a/Source/OpenTK/Platform/Windows/API.cs +++ b/Source/OpenTK/Platform/Windows/API.cs @@ -59,6 +59,7 @@ namespace OpenTK.Platform.Windows using RECT = OpenTK.Platform.Windows.Win32Rectangle; using WNDPROC = System.IntPtr; using LPDEVMODE = DeviceMode; + using HDEVNOTIFY = System.IntPtr; using HRESULT = System.IntPtr; using HMONITOR = System.IntPtr; @@ -349,7 +350,7 @@ namespace OpenTK.Platform.Windows #region GetMessage /// - /// Low-level WINAPI function that retriives the next message in the queue. + /// Low-level WINAPI function that retrieves the next message in the queue. /// /// The pending message (if any) is stored here. /// Not used @@ -845,12 +846,22 @@ namespace OpenTK.Platform.Windows #endregion - [DllImport("user32.dll", SetLastError=true)] + [DllImport("user32.dll", SetLastError = true)] public static extern BOOL SetForegroundWindow(HWND hWnd); [DllImport("user32.dll", SetLastError = true)] public static extern BOOL BringWindowToTop(HWND hWnd); + [DllImport("user32.dll", SetLastError = true)] + public static extern BOOL SetParent(HWND child, HWND newParent); + + [DllImport("user32.dll", SetLastError = true)] + public static extern HDEVNOTIFY RegisterDeviceNotification(HANDLE hRecipient, + LPVOID NotificationFilter, DeviceNotification Flags); + + [DllImport("user32.dll", SetLastError = true)] + public static extern BOOL UnregisterDeviceNotification(HDEVNOTIFY Handle); + #endregion #region Display settings @@ -951,6 +962,18 @@ namespace OpenTK.Platform.Windows [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] public static extern IntPtr SetFocus(IntPtr hwnd); + [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] + public static extern int ShowCursor(bool show); + + [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] + public static extern bool ClipCursor(ref RECT rcClip); + + [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] + public static extern bool ClipCursor(IntPtr rcClip); + + [DllImport("user32.dll")] + public static extern bool SetCursorPos(int X, int Y); + #region Async input #region GetCursorPos @@ -966,7 +989,7 @@ namespace OpenTK.Platform.Windows /// The input desktop must be the current desktop when you call GetCursorPos. Call OpenInputDesktop to determine whether the current desktop is the input desktop. If it is not, call SetThreadDesktop with the HDESK returned by OpenInputDesktop to switch to that desktop. /// [DllImport("user32.dll", SetLastError = true), SuppressUnmanagedCodeSecurity] - internal static extern BOOL GetCursorPos(ref Point point); + internal static extern BOOL GetCursorPos(ref POINT point); #endregion @@ -1344,17 +1367,6 @@ namespace OpenTK.Platform.Windows /// /// GetRawInputData gets the raw input one RawInput structure at a time. In contrast, GetRawInputBuffer gets an array of RawInput structures. /// - [CLSCompliant(false)] - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("user32.dll", SetLastError = true)] - internal static extern UINT GetRawInputData( - HRAWINPUT RawInput, - GetRawInputDataEnum Command, - [Out] LPVOID Data, - [In, Out] ref UINT Size, - UINT SizeHeader - ); - [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("user32.dll", SetLastError = true)] internal static extern INT GetRawInputData( @@ -1386,17 +1398,6 @@ namespace OpenTK.Platform.Windows /// /// GetRawInputData gets the raw input one RawInput structure at a time. In contrast, GetRawInputBuffer gets an array of RawInput structures. /// - [CLSCompliant(false)] - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("user32.dll", SetLastError = true)] - internal static extern UINT GetRawInputData( - HRAWINPUT RawInput, - GetRawInputDataEnum Command, - /*[MarshalAs(UnmanagedType.LPStruct)]*/ [Out] out RawInput Data, - [In, Out] ref UINT Size, - UINT SizeHeader - ); - [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("user32.dll", SetLastError = true)] internal static extern INT GetRawInputData( @@ -1407,6 +1408,16 @@ namespace OpenTK.Platform.Windows INT SizeHeader ); + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("user32.dll", SetLastError = true)] + unsafe internal static extern INT GetRawInputData( + HRAWINPUT RawInput, + GetRawInputDataEnum Command, + RawInput* Data, + [In, Out] ref INT Size, + INT SizeHeader + ); + #endregion #region IntPtr NextRawInputStructure(IntPtr data) @@ -1478,7 +1489,7 @@ namespace OpenTK.Platform.Windows #region --- Constants --- - internal struct Constants + static class Constants { // Found in winuser.h internal const int KEYBOARD_OVERRUN_MAKE_CODE = 0xFF; @@ -1565,6 +1576,8 @@ namespace OpenTK.Platform.Windows // (found in winuser.h) internal const int ENUM_REGISTRY_SETTINGS = -2; internal const int ENUM_CURRENT_SETTINGS = -1; + + internal static readonly IntPtr MESSAGE_ONLY = new IntPtr(-3); } #endregion @@ -2222,25 +2235,11 @@ namespace OpenTK.Platform.Windows /// To get device specific information, call GetRawInputDeviceInfo with the hDevice from RAWINPUTHEADER. /// Raw input is available only when the application calls RegisterRawInputDevices with valid device specifications. /// - [StructLayout(LayoutKind.Sequential)] - internal struct RawInput + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct RawInput { - internal RawInputHeader Header; - internal RawInputData Data; - - internal byte[] ToByteArray() - { - unsafe - { - byte[] dump = new byte[API.RawInputSize]; - fixed (RawInputDeviceType* ptr = &Header.Type) - { - for (int i = 0; i < API.RawInputSize; i++) - dump[i] = *((byte*)ptr + i); - return dump; - } - } - } + public RawInputHeader Header; + public RawInputData Data; } [StructLayout(LayoutKind.Explicit)] @@ -2341,38 +2340,9 @@ namespace OpenTK.Platform.Windows /// /// Contains information about the state of the mouse. /// - [StructLayout(LayoutKind.Sequential, Pack=1)] + [StructLayout(LayoutKind.Explicit)] internal struct RawMouse { - //internal RawMouseFlags Flags; // USHORT in winuser.h, but only INT works -- USHORT returns 0. - USHORT flags; - byte for_alignment; // Not used -- used for alignment - /// - /// Reserved. - /// - //ULONG Buttons; - internal USHORT buttonFlags; - /// - /// If usButtonFlags is RI_MOUSE_WHEEL, this member is a signed value that specifies the wheel delta. - /// - internal USHORT ButtonData;// { get { return (USHORT)((Buttons & 0xFFFF0000) >> 16); } } - /// - /// Raw state of the mouse buttons. - /// - internal ULONG RawButtons; - /// - /// Motion in the X direction. This is signed relative motion or absolute motion, depending on the value of usFlags. - /// - internal LONG LastX; - /// - /// Motion in the Y direction. This is signed relative motion or absolute motion, depending on the value of usFlags. - /// - internal LONG LastY; - /// - /// Device-specific additional information for the event. - /// - internal ULONG ExtraInformation; - /// /// Mouse state. This member can be any reasonable combination of the following. /// MOUSE_ATTRIBUTES_CHANGED @@ -2384,12 +2354,34 @@ namespace OpenTK.Platform.Windows /// MOUSE_VIRTUAL_DESKTOP /// Mouse coordinates are mapped to the virtual desktop (for a multiple monitor system). /// - internal RawMouseFlags Flags { get { return (RawMouseFlags)(flags); } } + [FieldOffset(0)] public RawMouseFlags Flags; // USHORT in winuser.h, but only INT works -- USHORT returns 0. + + [FieldOffset(4)] public RawInputMouseState ButtonFlags; /// - /// Transition state of the mouse buttons. + /// If usButtonFlags is RI_MOUSE_WHEEL, this member is a signed value that specifies the wheel delta. /// - internal RawInputMouseState ButtonFlags { get { return (RawInputMouseState)(buttonFlags); } } + [FieldOffset(6)] public USHORT ButtonData; + + /// + /// Raw state of the mouse buttons. + /// + [FieldOffset(8)] public ULONG RawButtons; + + /// + /// Motion in the X direction. This is signed relative motion or absolute motion, depending on the value of usFlags. + /// + [FieldOffset(12)] public LONG LastX; + + /// + /// Motion in the Y direction. This is signed relative motion or absolute motion, depending on the value of usFlags. + /// + [FieldOffset(16)] public LONG LastY; + + /// + /// Device-specific additional information for the event. + /// + [FieldOffset(20)] public ULONG ExtraInformation; } #endregion @@ -2474,10 +2466,11 @@ namespace OpenTK.Platform.Windows /// Number of HID inputs in bRawData. /// internal DWORD Count; - /// - /// Raw input data as an array of bytes. - /// - internal BYTE RawData; + // The RawData field must be marshalled manually. + ///// + ///// Raw input data as an array of bytes. + ///// + //internal IntPtr RawData; } #endregion @@ -2812,6 +2805,30 @@ namespace OpenTK.Platform.Windows #endregion + #region BroadcastHeader + + struct BroadcastHeader + { + public DWORD Size; + public DeviceBroadcastType DeviceType; + DWORD dbch_reserved; + } + + #endregion + + #region BroadcastDeviceInterface + + struct BroadcastDeviceInterface + { + public DWORD Size; + public DeviceBroadcastType DeviceType; + DWORD dbcc_reserved; + public Guid ClassGuid; + public char dbcc_name; + } + + #endregion + #endregion #region --- Enums --- @@ -3230,6 +3247,7 @@ namespace OpenTK.Platform.Windows /// /// Mouse indicator flags (found in winuser.h). /// + [Flags] internal enum RawMouseFlags : ushort { /// @@ -4147,6 +4165,30 @@ namespace OpenTK.Platform.Windows #endregion + #region DeviceNotification + + enum DeviceNotification + { + WINDOW_HANDLE = 0x00000000, + SERVICE_HANDLE = 0x00000001, + ALL_INTERFACE_CLASSES = 0x00000004, + } + + #endregion + + #region DeviceBroadcastType + + enum DeviceBroadcastType + { + OEM = 0, + VOLUME = 2, + PORT = 3, + INTERFACE = 5, + HANDLE = 6, + } + + #endregion + #endregion #region --- Callbacks --- @@ -4207,4 +4249,4 @@ namespace OpenTK.Platform.Windows #pragma warning restore 3019 #pragma warning restore 0649 #pragma warning restore 0169 -#pragma warning restore 0414 \ No newline at end of file +#pragma warning restore 0414 diff --git a/Source/OpenTK/Platform/Windows/WMInput.cs b/Source/OpenTK/Platform/Windows/WMInput.cs index 4015170e..e512204f 100644 --- a/Source/OpenTK/Platform/Windows/WMInput.cs +++ b/Source/OpenTK/Platform/Windows/WMInput.cs @@ -1,297 +1,175 @@ -#region --- License --- -/* Copyright (c) 2007 Stefanos Apostolopoulos - * See license.txt for license information - */ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 using System; using System.Collections.Generic; -using System.Text; -using System.Windows.Forms; - -using OpenTK.Input; using System.Diagnostics; using System.Drawing; +using System.Threading; +using System.Text; + +using OpenTK.Input; + namespace OpenTK.Platform.Windows { // Input driver for legacy (pre XP) Windows platforms. - sealed class WMInput : System.Windows.Forms.NativeWindow, IInputDriver + // Supports a single mouse and keyboard through async input. + // Supports multiple joysticks through WinMM. + sealed class WMInput : IInputDriver2, IMouseDriver2, IKeyboardDriver2, IGamePadDriver { - #region --- Fields --- + #region Fields - WinMMJoystick joystick_driver = new WinMMJoystick(); - // Driver supports only one keyboard and mouse; - KeyboardDevice keyboard = new KeyboardDevice(); - MouseDevice mouse = new MouseDevice(); - IList keyboards = new List(1); - IList mice = new List(1); - internal static readonly WinKeyMap KeyMap = new WinKeyMap(); - // Used to distinguish left and right control, alt and enter keys. - const long ExtendedBit = 1 << 24; - // Used to distinguish left and right shift keys. - static readonly uint ShiftRightScanCode = Functions.MapVirtualKey(VirtualKeys.RSHIFT, 0); + readonly object MouseLock = new object(); + readonly object KeyboardLock = new object(); + readonly WinMMJoystick gamepad_driver = new WinMMJoystick(); + KeyboardState keyboard = new KeyboardState(); + MouseState mouse = new MouseState(); + + readonly WinKeyMap KeyMap = new WinKeyMap(); #endregion - #region --- Constructor --- + #region Constructor - public WMInput(WinWindowInfo parent) + public WMInput() + : base() { - Debug.WriteLine("Initalizing WMInput driver."); - Debug.Indent(); - - AssignHandle(parent.WindowHandle); - Debug.Print("Input window attached to parent {0}", parent); - - Debug.Unindent(); - - keyboard.Description = "Standard Windows keyboard"; - keyboard.NumberOfFunctionKeys = 12; - keyboard.NumberOfKeys = 101; - keyboard.NumberOfLeds = 3; - - mouse.Description = "Standard Windows mouse"; - mouse.NumberOfButtons = 3; - mouse.NumberOfWheels = 1; - - keyboards.Add(keyboard); - mice.Add(mouse); + Debug.WriteLine("Using WMInput."); } #endregion - #region protected override void WndProc(ref Message msg) + #region Private Members - bool mouse_about_to_enter = false; - protected override void WndProc(ref Message msg) + void UpdateKeyboard() { - UIntPtr lparam, wparam; - unsafe + for (int i = 0; i < 256; i++) { - lparam = (UIntPtr)(void*)msg.LParam; - wparam = (UIntPtr)(void*)msg.WParam; - } - - switch ((WindowMessage)msg.Msg) - { - // Mouse events: - case WindowMessage.NCMOUSEMOVE: - mouse_about_to_enter = true; // Used to simulate a mouse enter event. - break; - - case WindowMessage.MOUSEMOVE: - mouse.Position = new Point( - (int)(lparam.ToUInt32() & 0x0000FFFF), - (int)(lparam.ToUInt32() & 0xFFFF0000) >> 16); - if (mouse_about_to_enter) - { - Cursor.Current = Cursors.Default; - mouse_about_to_enter = false; - } - return; - - case WindowMessage.MOUSEWHEEL: - // This is due to inconsistent behavior of the WParam value on 64bit arch, whese - // wparam = 0xffffffffff880000 or wparam = 0x00000000ff100000 - mouse.Wheel += (int)((long)msg.WParam << 32 >> 48) / 120; - return; - - case WindowMessage.LBUTTONDOWN: - mouse[MouseButton.Left] = true; - return; - - case WindowMessage.MBUTTONDOWN: - mouse[MouseButton.Middle] = true; - return; - - case WindowMessage.RBUTTONDOWN: - mouse[MouseButton.Right] = true; - return; - - case WindowMessage.XBUTTONDOWN: - mouse[((wparam.ToUInt32() & 0xFFFF0000) >> 16) != (int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = true; - return; - - case WindowMessage.LBUTTONUP: - mouse[MouseButton.Left] = false; - return; - - case WindowMessage.MBUTTONUP: - mouse[MouseButton.Middle] = false; - return; - - case WindowMessage.RBUTTONUP: - mouse[MouseButton.Right] = false; - return; - - case WindowMessage.XBUTTONUP: - // TODO: Is this correct? - mouse[((wparam.ToUInt32() & 0xFFFF0000) >> 16) != (int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = false; - return; - - // Keyboard events: - case WindowMessage.KEYDOWN: - case WindowMessage.KEYUP: - case WindowMessage.SYSKEYDOWN: - case WindowMessage.SYSKEYUP: - bool pressed = (WindowMessage)msg.Msg == WindowMessage.KEYDOWN || - (WindowMessage)msg.Msg == 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 = (msg.LParam.ToInt64() & ExtendedBit) != 0; - switch ((VirtualKeys)wparam) - { - case VirtualKeys.SHIFT: - // The behavior of this key is very strange. Unlike Control and Alt, there is no extended bit - // to distinguish between left and right keys. Moreover, pressing both keys and releasing one - // may result in both keys being held down (but not always). - // The only reliably way to solve this was reported by BlueMonkMN at the forums: we should - // check the scancodes. It looks like GLFW does the same thing, so it should be reliable. - - // TODO: Not 100% reliable, when both keys are pressed at once. - if (ShiftRightScanCode != 0) - { - unchecked - { - if (((lparam.ToUInt32() >> 16) & 0xFF) == ShiftRightScanCode) - keyboard[Input.Key.ShiftRight] = pressed; - else - keyboard[Input.Key.ShiftLeft] = pressed; - } - } - else - { - // Should only fall here on Windows 9x and NT4.0- - keyboard[Input.Key.ShiftLeft] = pressed; - } - return; - - case VirtualKeys.CONTROL: - if (extended) - keyboard[Input.Key.ControlRight] = pressed; - else - keyboard[Input.Key.ControlLeft] = pressed; - return; - - case VirtualKeys.MENU: - if (extended) - keyboard[Input.Key.AltRight] = pressed; - else - keyboard[Input.Key.AltLeft] = pressed; - return; - - case VirtualKeys.RETURN: - if (extended) - keyboard[Key.KeypadEnter] = pressed; - else - keyboard[Key.Enter] = pressed; - return; - - default: - if (!WMInput.KeyMap.ContainsKey((VirtualKeys)msg.WParam)) - { - Debug.Print("Virtual key {0} ({1}) not mapped.", (VirtualKeys)msg.WParam, (int)msg.WParam); - break; - } - else - { - keyboard[WMInput.KeyMap[(VirtualKeys)msg.WParam]] = pressed; - return; - } - } - break; - - case WindowMessage.KILLFOCUS: - keyboard.ClearKeys(); - break; - - case WindowMessage.DESTROY: - Debug.Print("Input window detached from parent {0}.", Handle); - ReleaseHandle(); - break; - - case WindowMessage.QUIT: - Debug.WriteLine("Input window quit."); - this.Dispose(); - break; - } - - base.WndProc(ref msg); - } - - #endregion - - #region --- IInputDriver Members --- - - #region IInputDriver Members - - public void Poll() - { - joystick_driver.Poll(); - } - - #endregion - - #region IKeyboardDriver Members - - public IList Keyboard - { - get { return keyboards; } - } - - #endregion - - #region IMouseDriver Members - - public IList Mouse - { - get { return mice; } - } - - #endregion - - #region IJoystickDriver Members - - public IList Joysticks - { - get { return joystick_driver.Joysticks; } - } - - #endregion - - #endregion - - #region --- IDisposable Members --- - - private bool disposed; - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool manual) - { - if (!disposed) - { - if (manual) - this.ReleaseHandle(); - - disposed = true; + VirtualKeys key = (VirtualKeys)i; + bool pressed = (Functions.GetAsyncKeyState(key) >> 8) != 0; + if (KeyMap.ContainsKey(key)) + { + keyboard[KeyMap[key]] = pressed; + } } } - ~WMInput() + void UpdateMouse() { - Dispose(false); + POINT p = new POINT(); + Functions.GetCursorPos(ref p); + mouse.X = p.X; + mouse.Y = p.Y; + // Note: we cannot poll the mouse wheel + mouse[MouseButton.Left] = (Functions.GetAsyncKeyState(VirtualKeys.LBUTTON) >> 8) != 0; + mouse[MouseButton.Middle] = (Functions.GetAsyncKeyState(VirtualKeys.RBUTTON) >> 8) != 0; + mouse[MouseButton.Right] = (Functions.GetAsyncKeyState(VirtualKeys.MBUTTON) >> 8) != 0; + mouse[MouseButton.Button1] = (Functions.GetAsyncKeyState(VirtualKeys.XBUTTON1) >> 8) != 0; + mouse[MouseButton.Button2] = (Functions.GetAsyncKeyState(VirtualKeys.XBUTTON2) >> 8) != 0; + } + + #endregion + + #region IInputDriver2 Members + + public IKeyboardDriver2 KeyboardDriver + { + get { return this; } + } + + public IMouseDriver2 MouseDriver + { + get { return this; } + } + + public IGamePadDriver GamePadDriver + { + get { return this; } + } + + #endregion + + #region IMouseDriver2 Members + + public MouseState GetState() + { + lock (MouseLock) + { + UpdateMouse(); + return mouse; + } + } + + public MouseState GetState(int index) + { + lock (MouseLock) + { + UpdateMouse(); + if (index == 0) + return mouse; + else + return new MouseState(); + } + } + + public void SetPosition(double x, double y) + { + Functions.SetCursorPos((int)x, (int)y); + } + + #endregion + + #region IKeyboardDriver2 Members + + KeyboardState IKeyboardDriver2.GetState() + { + lock (KeyboardLock) + { + UpdateKeyboard(); + return keyboard; + } + } + + KeyboardState IKeyboardDriver2.GetState(int index) + { + lock (KeyboardLock) + { + UpdateKeyboard(); + if (index == 0) + return keyboard; + else + return new KeyboardState(); + } + } + + string IKeyboardDriver2.GetDeviceName(int index) + { + return "Default Windows Keyboard"; } #endregion diff --git a/Source/OpenTK/Platform/Windows/WglHelper.cs b/Source/OpenTK/Platform/Windows/WglHelper.cs index dbdd4dfb..46231aca 100644 --- a/Source/OpenTK/Platform/Windows/WglHelper.cs +++ b/Source/OpenTK/Platform/Windows/WglHelper.cs @@ -24,9 +24,9 @@ namespace OpenTK.Platform.Windows delegatesClass = wglClass.GetNestedType("Delegates", BindingFlags.Static | BindingFlags.NonPublic); importsClass = wglClass.GetNestedType("Imports", BindingFlags.Static | BindingFlags.NonPublic); - // Ensure core entry points are ready prior to accessing any method. - // Resolves bug [#993]: "Possible bug in GraphicsContext.CreateDummyContext()" - LoadAll(); + //// Ensure core entry points are ready prior to accessing any method. + //// Resolves bug [#993]: "Possible bug in GraphicsContext.CreateDummyContext()" + //LoadAll(); } #endregion @@ -42,6 +42,8 @@ namespace OpenTK.Platform.Windows private static bool rebuildExtensionList = true; + static readonly object SyncRoot = new object(); + #endregion #region static Delegate LoadDelegate(string name, Type signature) @@ -108,7 +110,10 @@ namespace OpenTK.Platform.Windows /// public static void LoadAll() { - OpenTK.Platform.Utilities.LoadExtensions(typeof(Wgl)); + lock (SyncRoot) + { + OpenTK.Platform.Utilities.LoadExtensions(typeof(Wgl)); + } } #endregion diff --git a/Source/OpenTK/Platform/Windows/WinDisplayDevice.cs b/Source/OpenTK/Platform/Windows/WinDisplayDevice.cs index ec0025a8..195c119b 100644 --- a/Source/OpenTK/Platform/Windows/WinDisplayDevice.cs +++ b/Source/OpenTK/Platform/Windows/WinDisplayDevice.cs @@ -1,37 +1,101 @@ -#region --- License --- -/* Licensed under the MIT/X11 license. - * Copyright (c) 2006-2008 the OpenTK team. - * This notice may not be removed. - * See license.txt for licensing detailed licensing details. - */ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 using System; using System.Collections.Generic; -using System.Text; - -using OpenTK.Graphics; -using System.Runtime.InteropServices; using System.Diagnostics; namespace OpenTK.Platform.Windows { - internal class WinDisplayDeviceDriver : IDisplayDeviceDriver + sealed class WinDisplayDeviceDriver : DisplayDeviceBase { - static object display_lock = new object(); - static Dictionary available_device_names = - new Dictionary(); // Needed for ChangeDisplaySettingsEx + readonly object display_lock = new object(); - #region --- Constructors --- + #region Constructors - /// Queries available display devices and display resolutions. - static WinDisplayDeviceDriver() + public WinDisplayDeviceDriver() + { + RefreshDisplayDevices(); + Microsoft.Win32.SystemEvents.DisplaySettingsChanged += + HandleDisplaySettingsChanged; + } + + #endregion + + #region IDisplayDeviceDriver Members + + #region TryChangeResolution + + public sealed override bool TryChangeResolution(DisplayDevice device, DisplayResolution resolution) + { + DeviceMode mode = null; + + if (resolution != null) + { + mode = new DeviceMode(); + mode.PelsWidth = resolution.Width; + mode.PelsHeight = resolution.Height; + mode.BitsPerPel = resolution.BitsPerPixel; + mode.DisplayFrequency = (int)resolution.RefreshRate; + mode.Fields = Constants.DM_BITSPERPEL + | Constants.DM_PELSWIDTH + | Constants.DM_PELSHEIGHT + | Constants.DM_DISPLAYFREQUENCY; + } + + return Constants.DISP_CHANGE_SUCCESSFUL == + Functions.ChangeDisplaySettingsEx((string)device.Id, mode, IntPtr.Zero, + ChangeDisplaySettingsEnum.Fullscreen, IntPtr.Zero); + } + + #endregion + + #region TryRestoreResolution + + public sealed override bool TryRestoreResolution(DisplayDevice device) + { + return TryChangeResolution(device, null); + } + + #endregion + + #endregion + + #region Private Members + + #region RefreshDisplayDevices + + public void RefreshDisplayDevices() { lock (display_lock) { - // To minimize the need to add static methods to OpenTK.Graphics.DisplayDevice - // we only allow settings to be set through its constructor. - // Thus, we save all necessary parameters in temporary variables + AvailableDevices.Clear(); + + // We save all necessary parameters in temporary variables // and construct the device when every needed detail is available. // The main DisplayDevice constructor adds the newly constructed device // to the list of available devices. @@ -82,56 +146,37 @@ namespace OpenTK.Platform.Windows opentk_dev_current_res, opentk_dev_primary, opentk_dev_available_res, - opentk_dev_current_res.Bounds); + opentk_dev_current_res.Bounds, + dev1.DeviceName); - available_device_names.Add(opentk_dev, dev1.DeviceName); + AvailableDevices.Add(opentk_dev); + + if (opentk_dev_primary) + Primary = opentk_dev; + + Debug.Print("DisplayDevice {0} ({1}) supports {2} resolutions.", + device_count, opentk_dev.IsPrimary ? "primary" : "secondary", opentk_dev.AvailableResolutions.Count); } } } - public WinDisplayDeviceDriver() + #endregion + + #region HandleDisplaySettingsChanged + + void HandleDisplaySettingsChanged(object sender, EventArgs e) { + RefreshDisplayDevices(); } #endregion - #region --- IDisplayDeviceDriver Members --- - - #region public bool TryChangeResolution(OpenTK.Graphics.DisplayDevice device, DisplayResolution resolution) - - public bool TryChangeResolution(DisplayDevice device, DisplayResolution resolution) + ~WinDisplayDeviceDriver() { - DeviceMode mode = null; - - if (resolution != null) - { - mode = new DeviceMode(); - mode.PelsWidth = resolution.Width; - mode.PelsHeight = resolution.Height; - mode.BitsPerPel = resolution.BitsPerPixel; - mode.DisplayFrequency = (int)resolution.RefreshRate; - mode.Fields = Constants.DM_BITSPERPEL - | Constants.DM_PELSWIDTH - | Constants.DM_PELSHEIGHT - | Constants.DM_DISPLAYFREQUENCY; - } - - return Constants.DISP_CHANGE_SUCCESSFUL == - Functions.ChangeDisplaySettingsEx(available_device_names[device], mode, IntPtr.Zero, - ChangeDisplaySettingsEnum.Fullscreen, IntPtr.Zero); + Microsoft.Win32.SystemEvents.DisplaySettingsChanged -= + HandleDisplaySettingsChanged; } #endregion - - #region public TryRestoreResolution TryRestoreResolution(OpenTK.Graphics.DisplayDevice device) - - public bool TryRestoreResolution(DisplayDevice device) - { - return TryChangeResolution(device, null); - } - - #endregion - - #endregion } } diff --git a/Source/OpenTK/Platform/Windows/WinFactory.cs b/Source/OpenTK/Platform/Windows/WinFactory.cs index 461e5be2..aa802fdf 100644 --- a/Source/OpenTK/Platform/Windows/WinFactory.cs +++ b/Source/OpenTK/Platform/Windows/WinFactory.cs @@ -36,6 +36,9 @@ namespace OpenTK.Platform.Windows class WinFactory : IPlatformFactory { + readonly object SyncRoot = new object(); + IInputDriver2 inputDriver; + #region IPlatformFactory Members public virtual INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) @@ -71,16 +74,37 @@ namespace OpenTK.Platform.Windows return new WinGraphicsMode(); } - public virtual OpenTK.Input.IKeyboardDriver CreateKeyboardDriver() + public virtual OpenTK.Input.IKeyboardDriver2 CreateKeyboardDriver() { - throw new NotImplementedException(); - // If Windows version is NT5 or higher, we are able to use raw input. - if (System.Environment.OSVersion.Version.Major >= 5) - return new WinRawKeyboard(); - else - return new WMInput(null); + return InputDriver.KeyboardDriver; + } + + public virtual OpenTK.Input.IMouseDriver2 CreateMouseDriver() + { + return InputDriver.MouseDriver; } #endregion + + IInputDriver2 InputDriver + { + get + { + lock (SyncRoot) + { + if (inputDriver == null) + { + // If Windows version is NT5 or higher, we are able to use raw input. + if (System.Environment.OSVersion.Version.Major > 5 || + (System.Environment.OSVersion.Version.Major == 5 && + System.Environment.OSVersion.Version.Minor > 0)) + inputDriver = new WinRawInput(); + else + inputDriver = new WMInput(); + } + return inputDriver; + } + } + } } } diff --git a/Source/OpenTK/Platform/Windows/WinGLContext.cs b/Source/OpenTK/Platform/Windows/WinGLContext.cs index 74396a52..6dcf9d06 100644 --- a/Source/OpenTK/Platform/Windows/WinGLContext.cs +++ b/Source/OpenTK/Platform/Windows/WinGLContext.cs @@ -27,7 +27,8 @@ namespace OpenTK.Platform.Windows /// internal sealed class WinGLContext : DesktopGraphicsContext { - static object SyncRoot = new object(); + static readonly object LoadLock = new object(); + static readonly object SyncRoot = new object(); static IntPtr opengl32Handle; static bool wgl_loaded; @@ -39,18 +40,7 @@ namespace OpenTK.Platform.Windows static WinGLContext() { - lock (SyncRoot) - { - // Dynamically load the OpenGL32.dll in order to use the extension loading capabilities of Wgl. - if (opengl32Handle == IntPtr.Zero) - { - opengl32Handle = Functions.LoadLibrary(opengl32Name); - if (opengl32Handle == IntPtr.Zero) - throw new ApplicationException(String.Format("LoadLibrary(\"{0}\") call failed with code {1}", - opengl32Name, Marshal.GetLastWin32Error())); - Debug.WriteLine(String.Format("Loaded opengl32.dll: {0}", opengl32Handle)); - } - } + Init(); } public WinGLContext(GraphicsMode format, WinWindowInfo window, IGraphicsContext sharedContext, @@ -68,60 +58,60 @@ namespace OpenTK.Platform.Windows Mode = format; - Debug.Print("OpenGL will be bound to handle: {0}", window.WindowHandle); - Debug.Write("Setting pixel format... "); + Debug.Print("OpenGL will be bound to window:{0} on thread:{1}", window.WindowHandle, + System.Threading.Thread.CurrentThread.ManagedThreadId); this.SetGraphicsModePFD(format, (WinWindowInfo)window); - if (!wgl_loaded) + lock (LoadLock) { - // We need to create a temp context in order to load wgl extensions (e.g. for multisampling or GL3). - // We cannot rely on OpenTK.Platform.Wgl until we create the context and call Wgl.LoadAll(). - Debug.Print("Creating temporary context for wgl extensions."); - - ContextHandle temp_context = new ContextHandle(Wgl.Imports.CreateContext(window.DeviceContext)); - Wgl.Imports.MakeCurrent(window.DeviceContext, temp_context.Handle); - Wgl.LoadAll(); - Wgl.MakeCurrent(IntPtr.Zero, IntPtr.Zero); - Wgl.DeleteContext(temp_context.Handle); - wgl_loaded = true; - } - - if (Wgl.Delegates.wglCreateContextAttribsARB != null) - { - try + if (!wgl_loaded) { - Debug.Write("Using WGL_ARB_create_context... "); - - List attributes = new List(); - attributes.Add((int)ArbCreateContext.MajorVersion); - attributes.Add(major); - attributes.Add((int)ArbCreateContext.MinorVersion); - attributes.Add(minor); - if (flags != 0) - { - attributes.Add((int)ArbCreateContext.Flags); -#warning "This is not entirely correct: Embedded is not a valid flag! We need to add a GetARBContextFlags(GraphicsContextFlags) method." - attributes.Add((int)flags); - } - // According to the docs, " specifies a list of attributes for the context. - // The list consists of a sequence of pairs terminated by the - // value 0. [...]" - // Is this a single 0, or a <0, 0> pair? (Defensive coding: add two zeroes just in case). - attributes.Add(0); - attributes.Add(0); - - Handle = new ContextHandle( - Wgl.Arb.CreateContextAttribs( - window.DeviceContext, - sharedContext != null ? (sharedContext as IGraphicsContextInternal).Context.Handle : IntPtr.Zero, - attributes.ToArray())); - if (Handle == ContextHandle.Zero) - Debug.Print("failed. (Error: {0})", Marshal.GetLastWin32Error()); - else - Debug.Print("success!"); + // We need to create a temp context in order to load wgl extensions (e.g. for multisampling or GL3). + // We cannot rely on OpenTK.Platform.Wgl until we create the context and call Wgl.LoadAll(). + Debug.Print("Creating temporary context for wgl extensions."); + ContextHandle temp_context = new ContextHandle(Wgl.Imports.CreateContext(window.DeviceContext)); + Wgl.Imports.MakeCurrent(window.DeviceContext, temp_context.Handle); + Wgl.LoadAll(); + Wgl.Imports.MakeCurrent(IntPtr.Zero, IntPtr.Zero); + Wgl.Imports.DeleteContext(temp_context.Handle); + wgl_loaded = true; + } + + if (Wgl.Delegates.wglCreateContextAttribsARB != null) + { + try + { + Debug.Write("Using WGL_ARB_create_context... "); + + List attributes = new List(); + attributes.Add((int)ArbCreateContext.MajorVersion); + attributes.Add(major); + attributes.Add((int)ArbCreateContext.MinorVersion); + attributes.Add(minor); + if (flags != 0) + { + attributes.Add((int)ArbCreateContext.Flags); +#warning "This is not entirely correct: Embedded is not a valid flag! We need to add a GetARBContextFlags(GraphicsContextFlags) method." + attributes.Add((int)flags); + } + // According to the docs, " specifies a list of attributes for the context. + // The list consists of a sequence of pairs terminated by the + // value 0. [...]" + // Is this a single 0, or a <0, 0> pair? (Defensive coding: add two zeroes just in case). + attributes.Add(0); + attributes.Add(0); + + Handle = new ContextHandle( + Wgl.Arb.CreateContextAttribs( + window.DeviceContext, + sharedContext != null ? (sharedContext as IGraphicsContextInternal).Context.Handle : IntPtr.Zero, + attributes.ToArray())); + if (Handle == ContextHandle.Zero) + Debug.Print("failed. (Error: {0})", Marshal.GetLastWin32Error()); + } + catch (EntryPointNotFoundException e) { Debug.Print(e.ToString()); } + catch (NullReferenceException e) { Debug.Print(e.ToString()); } } - catch (EntryPointNotFoundException e) { Debug.Print(e.ToString()); } - catch (NullReferenceException e) { Debug.Print(e.ToString()); } } if (Handle == ContextHandle.Zero) @@ -142,7 +132,7 @@ namespace OpenTK.Platform.Windows if (sharedContext != null) { Marshal.GetLastWin32Error(); - Debug.Write("Sharing state with context {0}: ", sharedContext.ToString()); + Debug.Write(String.Format("Sharing state with context {0}: ", sharedContext)); bool result = Wgl.Imports.ShareLists((sharedContext as IGraphicsContextInternal).Context.Handle, Handle.Handle); Debug.WriteLine(result ? "success!" : "failed with win32 error " + Marshal.GetLastWin32Error()); } @@ -162,13 +152,13 @@ namespace OpenTK.Platform.Windows #endregion - #region --- IGraphicsContext Members --- + #region IGraphicsContext Members #region SwapBuffers public override void SwapBuffers() { - if (!Functions.SwapBuffers(Wgl.GetCurrentDC())) + if (!Functions.SwapBuffers(DeviceContext)) throw new GraphicsContextException(String.Format( "Failed to swap buffers for context {0} current. Error: {1}", this, Marshal.GetLastWin32Error())); } @@ -179,30 +169,36 @@ namespace OpenTK.Platform.Windows public override void MakeCurrent(IWindowInfo window) { - bool success; - - if (window != null) + lock (SyncRoot) + lock (LoadLock) { - if (((WinWindowInfo)window).WindowHandle == IntPtr.Zero) - throw new ArgumentException("window", "Must point to a valid window."); + bool success; - success = Wgl.Imports.MakeCurrent(((WinWindowInfo)window).DeviceContext, Handle.Handle); + if (window != null) + { + if (((WinWindowInfo)window).WindowHandle == IntPtr.Zero) + throw new ArgumentException("window", "Must point to a valid window."); + + success = Wgl.Imports.MakeCurrent(((WinWindowInfo)window).DeviceContext, Handle.Handle); + } + else + { + success = Wgl.Imports.MakeCurrent(IntPtr.Zero, IntPtr.Zero); + } + + if (!success) + throw new GraphicsContextException(String.Format( + "Failed to make context {0} current. Error: {1}", this, Marshal.GetLastWin32Error())); } - else - success = Wgl.Imports.MakeCurrent(IntPtr.Zero, IntPtr.Zero); - - if (!success) - throw new GraphicsContextException(String.Format( - "Failed to make context {0} current. Error: {1}", this, Marshal.GetLastWin32Error())); - } + #endregion #region IsCurrent public override bool IsCurrent { - get { return Wgl.GetCurrentContext() == Handle.Handle; } + get { return Wgl.Imports.GetCurrentContext() == Handle.Handle; } } #endregion @@ -216,12 +212,18 @@ namespace OpenTK.Platform.Windows { get { - return vsync_supported && Wgl.Ext.GetSwapInterval() != 0; + lock (LoadLock) + { + return vsync_supported && Wgl.Ext.GetSwapInterval() != 0; + } } set { - if (vsync_supported) - Wgl.Ext.SwapInterval(value ? 1 : 0); + lock (LoadLock) + { + if (vsync_supported) + Wgl.Ext.SwapInterval(value ? 1 : 0); + } } } @@ -231,9 +233,12 @@ namespace OpenTK.Platform.Windows public override void LoadAll() { - Wgl.LoadAll(); - vsync_supported = Wgl.Arb.SupportsExtension(this, "WGL_EXT_swap_control") && - Wgl.Load("wglGetSwapIntervalEXT") && Wgl.Load("wglSwapIntervalEXT"); + lock (LoadLock) + { + Wgl.LoadAll(); + vsync_supported = Wgl.Arb.SupportsExtension(this, "WGL_EXT_swap_control") && + Wgl.Load("wglGetSwapIntervalEXT") && Wgl.Load("wglSwapIntervalEXT"); + } base.LoadAll(); } @@ -242,7 +247,7 @@ namespace OpenTK.Platform.Windows #endregion - #region --- IGLContextInternal Members --- + #region IGLContextInternal Members #region IWindowInfo IGLContextInternal.Info /* @@ -264,12 +269,15 @@ namespace OpenTK.Platform.Windows #endregion - #region --- Private Methods --- + #region Private Methods - #region void SetGraphicsModePFD(GraphicsMode format, WinWindowInfo window) + #region SetGraphicsModePFD + // Note: there is no relevant ARB function. void SetGraphicsModePFD(GraphicsMode mode, WinWindowInfo window) { + Debug.Write("Setting pixel format... "); + if (!mode.Index.HasValue) throw new GraphicsModeException("Invalid or unsupported GraphicsMode."); @@ -283,20 +291,12 @@ namespace OpenTK.Platform.Windows throw new GraphicsContextException(String.Format( "Requested GraphicsMode not available. SetPixelFormat error: {0}", Marshal.GetLastWin32Error())); } - #endregion - - #region void SetGraphicsModeARB(GraphicsMode format, IWindowInfo window) - - void SetGraphicsModeARB(GraphicsMode format, IWindowInfo window) - { - throw new NotImplementedException(); - } #endregion #endregion - #region --- Internal Methods --- + #region Internal Methods #region internal IntPtr DeviceContext @@ -304,16 +304,32 @@ namespace OpenTK.Platform.Windows { get { - return Wgl.GetCurrentDC(); + return Wgl.Imports.GetCurrentDC(); } } #endregion + static internal void Init() + { + lock (SyncRoot) + { + // Dynamically load the OpenGL32.dll in order to use the extension loading capabilities of Wgl. + if (opengl32Handle == IntPtr.Zero) + { + opengl32Handle = Functions.LoadLibrary(opengl32Name); + if (opengl32Handle == IntPtr.Zero) + throw new ApplicationException(String.Format("LoadLibrary(\"{0}\") call failed with code {1}", + opengl32Name, Marshal.GetLastWin32Error())); + Debug.WriteLine(String.Format("Loaded opengl32.dll: {0}", opengl32Handle)); + } + } + } + #endregion - #region --- Overrides --- + #region Overrides /// Returns a System.String describing this OpenGL context. /// A System.String describing this OpenGL context. @@ -324,7 +340,7 @@ namespace OpenTK.Platform.Windows #endregion - #region --- IDisposable Members --- + #region IDisposable Members public override void Dispose() { diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index 438b49cf..61814c14 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -49,9 +49,11 @@ namespace OpenTK.Platform.Windows const ExtendedWindowStyle ParentStyleEx = ExtendedWindowStyle.WindowEdge | ExtendedWindowStyle.ApplicationWindow; const ExtendedWindowStyle ChildStyleEx = 0; + static readonly WinKeyMap KeyMap = new WinKeyMap(); readonly IntPtr Instance = Marshal.GetHINSTANCE(typeof(WinGLNative).Module); readonly IntPtr ClassName = Marshal.StringToHGlobalAuto(Guid.NewGuid().ToString()); readonly WindowProcedure WindowProcedureDelegate; + readonly uint ModalLoopTimerPeriod = 1; UIntPtr timer_handle; readonly Functions.TimerProc ModalLoopCallback; @@ -78,59 +80,62 @@ namespace OpenTK.Platform.Windows const ClassStyle DefaultClassStyle = ClassStyle.OwnDC; - readonly IntPtr DefaultWindowProcedure = - Marshal.GetFunctionPointerForDelegate(new WindowProcedure(Functions.DefWindowProc)); - // Used for IInputDriver implementation WinMMJoystick joystick_driver = new WinMMJoystick(); KeyboardDevice keyboard = new KeyboardDevice(); MouseDevice mouse = new MouseDevice(); IList keyboards = new List(1); IList mice = new List(1); - internal static readonly WinKeyMap KeyMap = new WinKeyMap(); const long ExtendedBit = 1 << 24; // Used to distinguish left and right control, alt and enter keys. static readonly uint ShiftRightScanCode = Functions.MapVirtualKey(VirtualKeys.RSHIFT, 0); // Used to distinguish left and right shift keys. KeyPressEventArgs key_press = new KeyPressEventArgs((char)0); + int cursor_visible_count = 0; + + static readonly object SyncRoot = new object(); + #endregion #region Contructors public WinGLNative(int x, int y, int width, int height, string title, GameWindowFlags options, DisplayDevice device) { - // This is the main window procedure callback. We need the callback in order to create the window, so - // don't move it below the CreateWindow calls. - WindowProcedureDelegate = WindowProcedure; + lock (SyncRoot) + { + // This is the main window procedure callback. We need the callback in order to create the window, so + // don't move it below the CreateWindow calls. + WindowProcedureDelegate = WindowProcedure; - //// This timer callback is called periodically when the window enters a sizing / moving modal loop. - //ModalLoopCallback = delegate(IntPtr handle, WindowMessage msg, UIntPtr eventId, int time) - //{ - // // Todo: find a way to notify the frontend that it should process queued up UpdateFrame/RenderFrame events. - // if (Move != null) - // Move(this, EventArgs.Empty); - //}; + //// This timer callback is called periodically when the window enters a sizing / moving modal loop. + //ModalLoopCallback = delegate(IntPtr handle, WindowMessage msg, UIntPtr eventId, int time) + //{ + // // Todo: find a way to notify the frontend that it should process queued up UpdateFrame/RenderFrame events. + // if (Move != null) + // Move(this, EventArgs.Empty); + //}; - // To avoid issues with Ati drivers on Windows 6+ with compositing enabled, the context will not be - // bound to the top-level window, but rather to a child window docked in the parent. - window = new WinWindowInfo( - CreateWindow(x, y, width, height, title, options, device, IntPtr.Zero), null); - child_window = new WinWindowInfo( - CreateWindow(0, 0, ClientSize.Width, ClientSize.Height, title, options, device, window.WindowHandle), window); + // To avoid issues with Ati drivers on Windows 6+ with compositing enabled, the context will not be + // bound to the top-level window, but rather to a child window docked in the parent. + window = new WinWindowInfo( + CreateWindow(x, y, width, height, title, options, device, IntPtr.Zero), null); + child_window = new WinWindowInfo( + CreateWindow(0, 0, ClientSize.Width, ClientSize.Height, title, options, device, window.WindowHandle), window); - exists = true; + exists = true; - keyboard.Description = "Standard Windows keyboard"; - keyboard.NumberOfFunctionKeys = 12; - keyboard.NumberOfKeys = 101; - keyboard.NumberOfLeds = 3; + keyboard.Description = "Standard Windows keyboard"; + keyboard.NumberOfFunctionKeys = 12; + keyboard.NumberOfKeys = 101; + keyboard.NumberOfLeds = 3; - mouse.Description = "Standard Windows mouse"; - mouse.NumberOfButtons = 3; - mouse.NumberOfWheels = 1; + mouse.Description = "Standard Windows mouse"; + mouse.NumberOfButtons = 3; + mouse.NumberOfWheels = 1; - keyboards.Add(keyboard); - mice.Add(mouse); + keyboards.Add(keyboard); + mice.Add(mouse); + } } #endregion @@ -154,7 +159,7 @@ namespace OpenTK.Platform.Windows else focused = (wParam.ToInt64() & 0xFFFF) != 0; - if (new_focused_state != Focused && FocusedChanged != null) + if (new_focused_state != Focused) FocusedChanged(this, EventArgs.Empty); break; @@ -186,8 +191,7 @@ namespace OpenTK.Platform.Windows if (Location != new_location) { bounds.Location = new_location; - if (Move != null) - Move(this, EventArgs.Empty); + Move(this, EventArgs.Empty); } Size new_size = new Size(pos->cx, pos->cy); @@ -204,9 +208,13 @@ namespace OpenTK.Platform.Windows SetWindowPosFlags.NOZORDER | SetWindowPosFlags.NOOWNERZORDER | SetWindowPosFlags.NOACTIVATE | SetWindowPosFlags.NOSENDCHANGING); - if (suppress_resize <= 0 && Resize != null) + if (suppress_resize <= 0) Resize(this, EventArgs.Empty); } + + // Ensure cursor remains grabbed + if (!CursorVisible) + GrabCursor(); } } break; @@ -226,6 +234,10 @@ namespace OpenTK.Platform.Windows } } + // Ensure cursor remains grabbed + if (!CursorVisible) + GrabCursor(); + break; case WindowMessage.SIZE: @@ -244,10 +256,13 @@ namespace OpenTK.Platform.Windows if (new_state != windowState) { windowState = new_state; - if (WindowStateChanged != null) - WindowStateChanged(this, EventArgs.Empty); + WindowStateChanged(this, EventArgs.Empty); } + // Ensure cursor remains grabbed + if (!CursorVisible) + GrabCursor(); + break; #endregion @@ -260,8 +275,7 @@ namespace OpenTK.Platform.Windows else key_press.KeyChar = (char)wParam.ToInt64(); - if (KeyPress != null) - KeyPress(this, key_press); + KeyPress(this, key_press); break; case WindowMessage.MOUSEMOVE: @@ -277,8 +291,7 @@ namespace OpenTK.Platform.Windows mouse_outside_window = false; EnableMouseTracking(); - if (MouseEnter != null) - MouseEnter(this, EventArgs.Empty); + MouseEnter(this, EventArgs.Empty); } break; @@ -286,8 +299,7 @@ namespace OpenTK.Platform.Windows mouse_outside_window = true; // Mouse tracking is disabled automatically by the OS - if (MouseLeave != null) - MouseLeave(this, EventArgs.Empty); + MouseLeave(this, EventArgs.Empty); break; case WindowMessage.MOUSEWHEEL: @@ -297,36 +309,45 @@ namespace OpenTK.Platform.Windows break; case WindowMessage.LBUTTONDOWN: + Functions.SetCapture(window.WindowHandle); mouse[MouseButton.Left] = true; break; case WindowMessage.MBUTTONDOWN: + Functions.SetCapture(window.WindowHandle); mouse[MouseButton.Middle] = true; break; case WindowMessage.RBUTTONDOWN: + Functions.SetCapture(window.WindowHandle); mouse[MouseButton.Right] = true; break; case WindowMessage.XBUTTONDOWN: - mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) != (int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = true; + Functions.SetCapture(window.WindowHandle); + mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) != + (int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = true; break; case WindowMessage.LBUTTONUP: + Functions.ReleaseCapture(); mouse[MouseButton.Left] = false; break; case WindowMessage.MBUTTONUP: + Functions.ReleaseCapture(); mouse[MouseButton.Middle] = false; break; case WindowMessage.RBUTTONUP: + Functions.ReleaseCapture(); mouse[MouseButton.Right] = false; break; case WindowMessage.XBUTTONUP: - // TODO: Is this correct? - mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) != (int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = false; + Functions.ReleaseCapture(); + mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) != + (int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = false; break; // Keyboard events: @@ -352,11 +373,12 @@ namespace OpenTK.Platform.Windows // The behavior of this key is very strange. Unlike Control and Alt, there is no extended bit // to distinguish between left and right keys. Moreover, pressing both keys and releasing one // may result in both keys being held down (but not always). - // The only reliably way to solve this was reported by BlueMonkMN at the forums: we should + // The only reliable way to solve this was reported by BlueMonkMN at the forums: we should // check the scancodes. It looks like GLFW does the same thing, so it should be reliable. - // TODO: Not 100% reliable, when both keys are pressed at once. - if (ShiftRightScanCode != 0) + // Note: we release both keys when either shift is released. + // Otherwise, the state of one key might be stuck to pressed. + if (ShiftRightScanCode != 0 && pressed) { unchecked { @@ -368,8 +390,8 @@ namespace OpenTK.Platform.Windows } else { - // Should only fall here on Windows 9x and NT4.0- - keyboard[Input.Key.ShiftLeft] = pressed; + // Windows 9x and NT4.0 or key release event. + keyboard[Input.Key.ShiftLeft] = keyboard[Input.Key.ShiftRight] = pressed; } return IntPtr.Zero; @@ -395,14 +417,14 @@ namespace OpenTK.Platform.Windows return IntPtr.Zero; default: - if (!WMInput.KeyMap.ContainsKey((VirtualKeys)wParam)) + if (!KeyMap.ContainsKey((VirtualKeys)wParam)) { - Debug.Print("Virtual key {0} ({1}) not mapped.", (VirtualKeys)wParam, (int)lParam); + Debug.Print("Virtual key {0} ({1}) not mapped.", (VirtualKeys)wParam, (long)lParam); break; } else { - keyboard[WMInput.KeyMap[(VirtualKeys)wParam]] = pressed; + keyboard[KeyMap[(VirtualKeys)wParam]] = pressed; } return IntPtr.Zero; } @@ -439,14 +461,10 @@ namespace OpenTK.Platform.Windows case WindowMessage.CLOSE: System.ComponentModel.CancelEventArgs e = new System.ComponentModel.CancelEventArgs(); - if (Closing != null) - Closing(this, e); + Closing(this, e); if (!e.Cancel) { - if (Unload != null) - Unload(this, EventArgs.Empty); - DestroyWindow(); break; } @@ -460,8 +478,7 @@ namespace OpenTK.Platform.Windows window.Dispose(); child_window.Dispose(); - if (Closed != null) - Closed(this, EventArgs.Empty); + Closed(this, EventArgs.Empty); break; @@ -628,6 +645,24 @@ namespace OpenTK.Platform.Windows suppress_resize--; } + void GrabCursor() + { + Win32Rectangle rect = Win32Rectangle.From(ClientRectangle); + Point pos = PointToScreen(new Point(rect.left, rect.top)); + rect.left = pos.X; + rect.top = pos.Y; + if (!Functions.ClipCursor(ref rect)) + Debug.WriteLine(String.Format("Failed to grab cursor. Error: {0}", + Marshal.GetLastWin32Error())); + } + + void UngrabCursor() + { + if (!Functions.ClipCursor(IntPtr.Zero)) + Debug.WriteLine(String.Format("Failed to ungrab cursor. Error: {0}", + Marshal.GetLastWin32Error())); + } + #endregion #region INativeWindow Members @@ -763,11 +798,15 @@ namespace OpenTK.Platform.Windows } set { - icon = value; - if (window.WindowHandle != IntPtr.Zero) + if (value != icon) { - Functions.SendMessage(window.WindowHandle, WindowMessage.SETICON, (IntPtr)0, icon == null ? IntPtr.Zero : value.Handle); - Functions.SendMessage(window.WindowHandle, WindowMessage.SETICON, (IntPtr)1, icon == null ? IntPtr.Zero : value.Handle); + icon = value; + if (window.WindowHandle != IntPtr.Zero) + { + Functions.SendMessage(window.WindowHandle, WindowMessage.SETICON, (IntPtr)0, icon == null ? IntPtr.Zero : value.Handle); + Functions.SendMessage(window.WindowHandle, WindowMessage.SETICON, (IntPtr)1, icon == null ? IntPtr.Zero : value.Handle); + } + IconChanged(this, EventArgs.Empty); } } } @@ -791,14 +830,18 @@ namespace OpenTK.Platform.Windows get { sb_title.Remove(0, sb_title.Length); - if (Functions.GetWindowText(window.WindowHandle, sb_title, sb_title.MaxCapacity) == 0) - Debug.Print("Failed to retrieve window title (window:{0}, reason:{2}).", window.WindowHandle, Marshal.GetLastWin32Error()); + if (Functions.GetWindowText(window.WindowHandle, sb_title, sb_title.Capacity) == 0) + Debug.Print("Failed to retrieve window title (window:{0}, reason:{1}).", window.WindowHandle, Marshal.GetLastWin32Error()); return sb_title.ToString(); } set { - if (!Functions.SetWindowText(window.WindowHandle, value)) - Debug.Print("Failed to change window title (window:{0}, new title:{1}, reason:{2}).", window.WindowHandle, value, Marshal.GetLastWin32Error()); + if (Title != value) + { + if (!Functions.SetWindowText(window.WindowHandle, value)) + Debug.Print("Failed to change window title (window:{0}, new title:{1}, reason:{2}).", window.WindowHandle, value, Marshal.GetLastWin32Error()); + TitleChanged(this, EventArgs.Empty); + } } } @@ -814,18 +857,23 @@ namespace OpenTK.Platform.Windows } set { - if (value) + if (value != Visible) { - Functions.ShowWindow(window.WindowHandle, ShowWindowCommand.SHOW); - if (invisible_since_creation) + if (value) { - Functions.BringWindowToTop(window.WindowHandle); - Functions.SetForegroundWindow(window.WindowHandle); + Functions.ShowWindow(window.WindowHandle, ShowWindowCommand.SHOW); + if (invisible_since_creation) + { + Functions.BringWindowToTop(window.WindowHandle); + Functions.SetForegroundWindow(window.WindowHandle); + } } - } - else if (!value) - { - Functions.ShowWindow(window.WindowHandle, ShowWindowCommand.HIDE); + else if (!value) + { + Functions.ShowWindow(window.WindowHandle, ShowWindowCommand.HIDE); + } + + VisibleChanged(this, EventArgs.Empty); } } } @@ -836,6 +884,38 @@ namespace OpenTK.Platform.Windows public bool Exists { get { return exists; } } + #endregion + + #region CursorVisible + + public bool CursorVisible + { + get { return cursor_visible_count >= 0; } // Not used + set + { + if (value && cursor_visible_count < 0) + { + do + { + cursor_visible_count = Functions.ShowCursor(true); + } + while (cursor_visible_count < 0); + + UngrabCursor(); + } + else if (!value && cursor_visible_count >= 0) + { + do + { + cursor_visible_count = Functions.ShowCursor(false); + } + while (cursor_visible_count >= 0); + + GrabCursor(); + } + } + } + #endregion #region Close @@ -1012,8 +1092,7 @@ namespace OpenTK.Platform.Windows WindowState = state; - if (WindowBorderChanged != null) - WindowBorderChanged(this, EventArgs.Empty); + WindowBorderChanged(this, EventArgs.Empty); } } @@ -1025,7 +1104,7 @@ namespace OpenTK.Platform.Windows { if (!Functions.ScreenToClient(window.WindowHandle, ref point)) throw new InvalidOperationException(String.Format( - "Could not convert point {0} from client to screen coordinates. Windows error: {1}", + "Could not convert point {0} from screen to client coordinates. Windows error: {1}", point.ToString(), Marshal.GetLastWin32Error())); return point; @@ -1035,52 +1114,36 @@ namespace OpenTK.Platform.Windows #region PointToScreen - public Point PointToScreen(Point p) + public Point PointToScreen(Point point) { - throw new NotImplementedException(); + if (!Functions.ClientToScreen(window.WindowHandle, ref point)) + throw new InvalidOperationException(String.Format( + "Could not convert point {0} from screen to client coordinates. Windows error: {1}", + point.ToString(), Marshal.GetLastWin32Error())); + + return point; } #endregion #region Events - public event EventHandler Idle; - - public event EventHandler Load; - - public event EventHandler Unload; - - public event EventHandler Move; - - public event EventHandler Resize; - - public event EventHandler Closing; - - public event EventHandler Closed; - - public event EventHandler Disposed; - - public event EventHandler IconChanged; - - public event EventHandler TitleChanged; - - public event EventHandler ClientSizeChanged; - - public event EventHandler VisibleChanged; - - public event EventHandler WindowInfoChanged; - - public event EventHandler FocusedChanged; - - public event EventHandler WindowBorderChanged; - - public event EventHandler WindowStateChanged; - - public event EventHandler KeyPress; - - public event EventHandler MouseEnter; - - public event EventHandler MouseLeave; + public event EventHandler Move = delegate { }; + public event EventHandler Resize = delegate { }; + public event EventHandler Closing = delegate { }; + public event EventHandler Closed = delegate { }; + public event EventHandler Disposed = delegate { }; + public event EventHandler IconChanged = delegate { }; + public event EventHandler TitleChanged = delegate { }; + public event EventHandler VisibleChanged = delegate { }; + public event EventHandler FocusedChanged = delegate { }; + public event EventHandler WindowBorderChanged = delegate { }; + public event EventHandler WindowStateChanged = delegate { }; + public event EventHandler KeyDown = delegate { }; + public event EventHandler KeyPress = delegate { }; + public event EventHandler KeyUp = delegate { }; + public event EventHandler MouseEnter = delegate { }; + public event EventHandler MouseLeave = delegate { }; #endregion @@ -1147,6 +1210,16 @@ namespace OpenTK.Platform.Windows get { return keyboards; } } + public KeyboardState GetState() + { + throw new NotImplementedException(); + } + + public KeyboardState GetState(int index) + { + throw new NotImplementedException(); + } + #endregion #region IMouseDriver Members @@ -1191,6 +1264,7 @@ namespace OpenTK.Platform.Windows Debug.Print("[Warning] INativeWindow leaked ({0}). Did you forget to call INativeWindow.Dispose()?", this); } + Disposed(this, EventArgs.Empty); disposed = true; } } diff --git a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs index 5881b5a4..69d0be95 100644 --- a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs +++ b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs @@ -2,7 +2,7 @@ // // The Open Toolkit Library License // -// Copyright (c) 2006 - 2009 the Open Toolkit library. +// Copyright (c) 2006 - 2010 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 @@ -28,144 +28,158 @@ using System; using System.Collections.Generic; using System.Text; -using System.Windows.Forms; using System.Diagnostics; using System.Runtime.InteropServices; using OpenTK.Graphics; -using ColorDepth = OpenTK.Graphics.ColorFormat; namespace OpenTK.Platform.Windows { - internal class WinGraphicsMode : IGraphicsMode + class WinGraphicsMode : IGraphicsMode { - // Todo: Get rid of the System.Windows.Forms.Control dependency. + #region Fields - #region --- Fields --- - - // To avoid recursion when calling GraphicsMode.Default - bool creating; + readonly List modes = new List(); + static readonly object SyncRoot = new object(); #endregion - #region --- Constructors --- + #region Constructors public WinGraphicsMode() { + lock (SyncRoot) + { + using (INativeWindow native = new NativeWindow()) + { + modes.AddRange(GetModesARB(native)); + if (modes.Count == 0) + modes.AddRange(GetModesPFD(native)); + if (modes.Count == 0) + throw new GraphicsModeException( + "No GraphicsMode available. This should never happen, please report a bug at http://www.opentk.com"); + } + modes.Sort(new GraphicsModeComparer()); + } } #endregion - #region --- IGraphicsMode Members --- + #region IGraphicsMode Members - public GraphicsMode SelectGraphicsMode(ColorDepth color, int depth, int stencil, int samples, ColorDepth accum, - int buffers, bool stereo) + public GraphicsMode SelectGraphicsMode(ColorFormat color, int depth, int stencil, int samples, + ColorFormat accum, int buffers, bool stereo) { GraphicsMode mode = null; - if (!creating) + do { - try + mode = modes.Find(delegate(GraphicsMode current) { - creating = true; - mode = SelectGraphicsModeARB(color, depth, stencil, samples, accum, buffers, stereo); - } - finally - { - creating = false; - } - } + return ModeSelector(current, color, depth, stencil, samples, accum, buffers, stereo); + }); + } while (mode == null && RelaxParameters( + ref color, ref depth, ref stencil, ref samples, ref accum, ref buffers, ref stereo)); + if (mode == null) - mode = SelectGraphicsModePFD(color, depth, stencil, samples, accum, buffers, stereo); + 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. + } + #endregion - #region --- Private Methods --- + #region Private Methods - #region SelectGraphicsModePFD + #region DescribePixelFormat - GraphicsMode SelectGraphicsModePFD(ColorDepth color, int depth, int stencil, int samples, ColorDepth accum, - int buffers, bool stereo) + static int DescribePixelFormat(IntPtr hdc, int ipfd, int cjpfd, ref PixelFormatDescriptor pfd) { - using (Control native_window = new Control()) - using (WinWindowInfo window = new WinWindowInfo(native_window.Handle, null)) + unsafe { - IntPtr deviceContext = ((WinWindowInfo)window).DeviceContext; - Debug.WriteLine(String.Format("Device context: {0}", deviceContext)); - - Debug.Write("Selecting pixel format... "); - PixelFormatDescriptor pixelFormat = new PixelFormatDescriptor(); - pixelFormat.Size = API.PixelFormatDescriptorSize; - pixelFormat.Version = API.PixelFormatDescriptorVersion; - pixelFormat.Flags = - PixelFormatDescriptorFlags.SUPPORT_OPENGL | - PixelFormatDescriptorFlags.DRAW_TO_WINDOW; - pixelFormat.ColorBits = (byte)(color.Red + color.Green + color.Blue); - - pixelFormat.PixelType = color.IsIndexed ? PixelType.INDEXED : PixelType.RGBA; - pixelFormat.RedBits = (byte)color.Red; - pixelFormat.GreenBits = (byte)color.Green; - pixelFormat.BlueBits = (byte)color.Blue; - pixelFormat.AlphaBits = (byte)color.Alpha; - - if (accum.BitsPerPixel > 0) + fixed (PixelFormatDescriptor* ppfd = &pfd) { - pixelFormat.AccumBits = (byte)(accum.Red + accum.Green + accum.Blue); - pixelFormat.AccumRedBits = (byte)accum.Red; - pixelFormat.AccumGreenBits = (byte)accum.Green; - pixelFormat.AccumBlueBits = (byte)accum.Blue; - pixelFormat.AccumAlphaBits = (byte)accum.Alpha; + // Note: DescribePixelFormat found in gdi32 is extremely slow + // on nvidia, for some reason. + return Wgl.Imports.DescribePixelFormat(hdc, ipfd, (uint)cjpfd, ppfd); } - - pixelFormat.DepthBits = (byte)depth; - pixelFormat.StencilBits = (byte)stencil; - - if (depth <= 0) pixelFormat.Flags |= PixelFormatDescriptorFlags.DEPTH_DONTCARE; - if (stereo) pixelFormat.Flags |= PixelFormatDescriptorFlags.STEREO; - if (buffers > 1) pixelFormat.Flags |= PixelFormatDescriptorFlags.DOUBLEBUFFER; - - int pixel = Functions.ChoosePixelFormat(deviceContext, ref pixelFormat); - if (pixel == 0) - throw new GraphicsModeException("The requested GraphicsMode is not available."); - - // Find out what we really got as a format: - PixelFormatDescriptor pfd = new PixelFormatDescriptor(); - pixelFormat.Size = API.PixelFormatDescriptorSize; - pixelFormat.Version = API.PixelFormatDescriptorVersion; - Functions.DescribePixelFormat(deviceContext, pixel, API.PixelFormatDescriptorSize, ref pfd); - GraphicsMode fmt = new GraphicsMode((IntPtr)pixel, - new ColorDepth(pfd.RedBits, pfd.GreenBits, pfd.BlueBits, pfd.AlphaBits), - pfd.DepthBits, - pfd.StencilBits, - 0, - new ColorDepth(pfd.AccumBits), - (pfd.Flags & PixelFormatDescriptorFlags.DOUBLEBUFFER) != 0 ? 2 : 1, - (pfd.Flags & PixelFormatDescriptorFlags.STEREO) != 0); - - return fmt; } } #endregion - #region SelectGraphicsModeARB + #region GetModesPFD - GraphicsMode SelectGraphicsModeARB(ColorDepth color, int depth, int stencil, int samples, ColorDepth accum, - int buffers, bool stereo) + IEnumerable GetModesPFD(INativeWindow native) { - using (INativeWindow native_window = new NativeWindow()) - using (IGraphicsContext context = new GraphicsContext(new GraphicsMode(new ColorFormat(), 0, 0, 0, new ColorFormat(), 2, false), native_window.WindowInfo, 1, 0, GraphicsContextFlags.Default)) - { - WinWindowInfo window = (WinWindowInfo)native_window.WindowInfo; + WinWindowInfo window = native.WindowInfo as WinWindowInfo; + IntPtr deviceContext = ((WinWindowInfo)window).DeviceContext; + Debug.WriteLine(String.Format("Device context: {0}", deviceContext)); - // See http://www.opengl.org/registry/specs/ARB/wgl_pixel_format.txt + 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; + + int pixel = 0; + while (DescribePixelFormat(deviceContext, ++pixel, API.PixelFormatDescriptorSize, ref pfd) != 0) + { + // Ignore non-accelerated formats. + if ((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 + + IEnumerable GetModesARB(INativeWindow native) + { + using (IGraphicsContext context = new GraphicsContext( + new GraphicsMode(new IntPtr(2), new ColorFormat(), 0, 0, 0, new ColorFormat(), 2, false), + (WinWindowInfo)native.WindowInfo, 1, 0, GraphicsContextFlags.Default)) + { + WinWindowInfo window = (WinWindowInfo)native.WindowInfo; + + // See http://www.opengl.org/registry/specs/ARB/wgl_pixel_format.txt // for more details - Debug.Write("Selecting pixel format (ARB)... "); + Debug.Write("Retrieving ARB pixel formats.... "); if (Wgl.Delegates.wglChoosePixelFormatARB == null || Wgl.Delegates.wglGetPixelFormatAttribivARB == null) { - Debug.WriteLine("failed"); - return null; + Debug.WriteLine("failed."); + yield break; } int[] attribs = new int[] @@ -199,74 +213,61 @@ namespace OpenTK.Platform.Windows int[] attribs_values = new int[] { - (int)WGL_ARB_pixel_format.AccelerationArb, (int)WGL_ARB_pixel_format.FullAccelerationArb, - (int)WGL_ARB_pixel_format.DrawToWindowArb, 1, - - (int)WGL_ARB_pixel_format.RedBitsArb, color.Red, - (int)WGL_ARB_pixel_format.GreenBitsArb, color.Green, - (int)WGL_ARB_pixel_format.BlueBitsArb, color.Blue, - (int)WGL_ARB_pixel_format.AlphaBitsArb, color.Alpha, - (int)WGL_ARB_pixel_format.ColorBitsArb, color.BitsPerPixel - color.Alpha, // Should not contain alpha bpp (see spec) - - (int)WGL_ARB_pixel_format.DepthBitsArb, depth, - (int)WGL_ARB_pixel_format.StencilBitsArb, stencil, - - (int)WGL_ARB_multisample.SampleBuffersArb, samples > 0 ? 1 : 0, - (int)WGL_ARB_multisample.SamplesArb, samples, - - (int)WGL_ARB_pixel_format.AccumRedBitsArb, accum.Red, - (int)WGL_ARB_pixel_format.AccumGreenBitsArb, accum.Green, - (int)WGL_ARB_pixel_format.AccumBlueBitsArb, accum.Blue, - (int)WGL_ARB_pixel_format.AccumAlphaBitsArb, accum.Alpha, - (int)WGL_ARB_pixel_format.AccumBitsArb, accum.BitsPerPixel, // Spec doesn't mention wether alpha bpp should be included... - - (int)WGL_ARB_pixel_format.DoubleBufferArb, buffers > 1 ? 1 : 0, - (int)WGL_ARB_pixel_format.StereoArb, stereo ? 1 : 0, + (int)WGL_ARB_pixel_format.AccelerationArb, + (int)WGL_ARB_pixel_format.FullAccelerationArb, 0, 0 }; - int[] pixel = new int[1], num_formats = new int[1]; - bool success = Wgl.Arb.ChoosePixelFormat(window.DeviceContext, attribs_values, null, 1, pixel, num_formats); - if (!success || num_formats[0] == 0 || pixel[0] == 0) - { - // Try again without an accumulator. Many modern cards cannot accelerate multisampled formats with accumulator buffers. - int index_of_accum = Array.IndexOf(attribs_values, (int)WGL_ARB_pixel_format.AccumRedBitsArb); - attribs_values[index_of_accum + 1] = attribs_values[index_of_accum + 3] = - attribs_values[index_of_accum + 5] = attribs_values[index_of_accum + 7] = - attribs_values[index_of_accum + 9] = 0; - Wgl.Arb.ChoosePixelFormat(window.DeviceContext, attribs_values, null, 1, pixel, num_formats); - } - if (!success || num_formats[0] == 0 || pixel[0] == 0) - { - Debug.WriteLine("failed (no suitable pixel format)."); - return null; - } + int[] num_formats = new int[1]; + Wgl.Arb.ChoosePixelFormat(window.DeviceContext, attribs_values, null, 0, null, num_formats); + int[] pixel = new int[num_formats[0]]; - // Find out what we really got as a format: - success = Wgl.Arb.GetPixelFormatAttrib(window.DeviceContext, pixel[0], 0, attribs.Length - 1, attribs, values); - if (!success) + if (Wgl.Arb.ChoosePixelFormat(window.DeviceContext, attribs_values, null, pixel.Length, pixel, num_formats)) { - Debug.WriteLine("failed (pixel format attributes could not be determined)."); - return null; - } - - GraphicsMode mode = new GraphicsMode(new IntPtr(pixel[0]), - new ColorDepth(values[1], values[2], values[3], values[4]), - values[6], - values[7], - values[8] != 0 ? values[9] : 0, - new ColorDepth(values[10], values[11], values[12], values[13]), - values[15] == 1 ? 2 : 1, - values[16] == 1 ? true : false); + foreach (int p in pixel) + { + // Find out what we really got as a format: + if (!Wgl.Arb.GetPixelFormatAttrib(window.DeviceContext, p, 0, attribs.Length - 1, attribs, values)) + { + Debug.Print("[Warning] Failed to detect attributes for PixelFormat:{0}.", p); + continue; + } - Debug.WriteLine("success!"); - return mode; + GraphicsMode mode = new GraphicsMode(new IntPtr(p), + new ColorFormat(values[1], values[2], values[3], values[4]), + values[6], + values[7], + values[8] != 0 ? values[9] : 0, + 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; + } + + #endregion + #endregion } } - diff --git a/Source/OpenTK/Platform/Windows/WinInputBase.cs b/Source/OpenTK/Platform/Windows/WinInputBase.cs new file mode 100644 index 00000000..408906b5 --- /dev/null +++ b/Source/OpenTK/Platform/Windows/WinInputBase.cs @@ -0,0 +1,203 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using OpenTK.Input; + +namespace OpenTK.Platform.Windows +{ + abstract class WinInputBase : IInputDriver2 + { + #region Fields + + readonly WindowProcedure WndProc; + readonly Thread InputThread; + readonly AutoResetEvent InputReady = new AutoResetEvent(false); + + IntPtr OldWndProc; + INativeWindow native; + + protected INativeWindow Native { get { return native; } private set { native = value; } } + protected WinWindowInfo Parent { get { return (WinWindowInfo)Native.WindowInfo; } } + + static readonly IntPtr Unhandled = new IntPtr(-1); + + #endregion + + #region Constructors + + public WinInputBase() + { + WndProc = WindowProcedure; + + InputThread = new Thread(ProcessEvents); + InputThread.IsBackground = true; + InputThread.Start(); + + InputReady.WaitOne(); + } + + #endregion + + #region Private Members + + #region ConstructMessageWindow + + INativeWindow ConstructMessageWindow() + { + Debug.WriteLine("Initializing input driver."); + Debug.Indent(); + + // Create a new message-only window to retrieve WM_INPUT messages. + INativeWindow native = new NativeWindow(); + native.ProcessEvents(); + WinWindowInfo parent = native.WindowInfo as WinWindowInfo; + Functions.SetParent(parent.WindowHandle, Constants.MESSAGE_ONLY); + native.ProcessEvents(); + + Debug.Unindent(); + return native; + } + + + #endregion + + #region ProcessEvents + + void ProcessEvents() + { + Native = ConstructMessageWindow(); + CreateDrivers(); + + // Subclass the window to retrieve the events we are interested in. + OldWndProc = Functions.SetWindowLong(Parent.WindowHandle, WndProc); + Debug.Print("Input window attached to {0}", Parent); + + InputReady.Set(); + + MSG msg = new MSG(); + while (Native.Exists) + { + int ret = Functions.GetMessage(ref msg, Parent.WindowHandle, 0, 0); + if (ret == -1) + { + throw new PlatformException(String.Format( + "An error happened while processing the message queue. Windows error: {0}", + Marshal.GetLastWin32Error())); + } + + Functions.TranslateMessage(ref msg); + Functions.DispatchMessage(ref msg); + } + } + + #endregion + + #region WndProcHandler + + IntPtr WndProcHandler( + IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + IntPtr ret = WindowProcedure(handle, message, wParam, lParam); + if (ret == Unhandled) + return Functions.CallWindowProc(OldWndProc, handle, message, wParam, lParam); + else + return ret; + } + + #endregion + + #endregion + + #region Protected Members + + #region WindowProcedure + + protected virtual IntPtr WindowProcedure( + IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + return Unhandled; + } + + #endregion + + #region CreateDrivers + + // Note: this method is called through the input thread. + protected abstract void CreateDrivers(); + + #endregion + + #endregion + + #region IInputDriver2 Members + + public abstract IMouseDriver2 MouseDriver { get; } + public abstract IKeyboardDriver2 KeyboardDriver { get; } + public abstract IGamePadDriver GamePadDriver { get; } + + #endregion + + #region IDisposable Members + + protected bool Disposed; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool manual) + { + if (!Disposed) + { + if (manual) + { + if (Native != null) + { + Native.Close(); + Native.Dispose(); + } + } + + Disposed = true; + } + } + + ~WinInputBase() + { + Debug.Print("[Warning] Resource leaked: {0}.", this); + Dispose(false); + } + + #endregion + } +} diff --git a/Source/OpenTK/Platform/Windows/WinKeyMap.cs b/Source/OpenTK/Platform/Windows/WinKeyMap.cs index 359a305a..93ab2bcf 100644 --- a/Source/OpenTK/Platform/Windows/WinKeyMap.cs +++ b/Source/OpenTK/Platform/Windows/WinKeyMap.cs @@ -1,113 +1,122 @@ -#region --- License --- -/* Copyright (c) 2007 Stefanos Apostolopoulos - * See license.txt for license information - */ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 using System; using System.Collections.Generic; -using System.Text; -using OpenTK.Platform.Windows; using OpenTK.Input; -using System.Diagnostics; namespace OpenTK.Platform.Windows { - internal class WinKeyMap : Dictionary + class WinKeyMap : Dictionary { /// /// Initializes the map between VirtualKeys and OpenTK.Key /// - internal WinKeyMap() + public WinKeyMap() { - try + this.Add(VirtualKeys.ESCAPE, Key.Escape); + + // Function keys + for (int i = 0; i < 24; i++) { - this.Add(VirtualKeys.ESCAPE, Key.Escape); - - // Function keys - for (int i = 0; i < 24; i++) - { - this.Add((VirtualKeys)((int)VirtualKeys.F1 + i), Key.F1 + i); - } - - // Number keys (0-9) - for (int i = 0; i <= 9; i++) - { - this.Add((VirtualKeys)(0x30 + i), Key.Number0 + i); - } - - // Letters (A-Z) - for (int i = 0; i < 26; i++) - { - this.Add((VirtualKeys)(0x41 + i), Key.A + i); - } - - this.Add(VirtualKeys.TAB, Key.Tab); - this.Add(VirtualKeys.CAPITAL, Key.CapsLock); - this.Add(VirtualKeys.LCONTROL, Key.ControlLeft); - this.Add(VirtualKeys.LSHIFT, Key.ShiftLeft); - this.Add(VirtualKeys.LWIN, Key.WinLeft); - this.Add(VirtualKeys.LMENU, Key.AltLeft); - this.Add(VirtualKeys.SPACE, Key.Space); - this.Add(VirtualKeys.RMENU, Key.AltRight); - this.Add(VirtualKeys.RWIN, Key.WinRight); - this.Add(VirtualKeys.APPS, Key.Menu); - this.Add(VirtualKeys.RCONTROL, Key.ControlRight); - this.Add(VirtualKeys.RSHIFT, Key.ShiftRight); - this.Add(VirtualKeys.RETURN, Key.Enter); - this.Add(VirtualKeys.BACK, Key.BackSpace); - - this.Add(VirtualKeys.OEM_1, Key.Semicolon); // Varies by keyboard, ;: on Win2K/US - this.Add(VirtualKeys.OEM_2, Key.Slash); // Varies by keyboard, /? on Win2K/US - this.Add(VirtualKeys.OEM_3, Key.Tilde); // Varies by keyboard, `~ on Win2K/US - this.Add(VirtualKeys.OEM_4, Key.BracketLeft); // Varies by keyboard, [{ on Win2K/US - this.Add(VirtualKeys.OEM_5, Key.BackSlash); // Varies by keyboard, \| on Win2K/US - this.Add(VirtualKeys.OEM_6, Key.BracketRight); // Varies by keyboard, ]} on Win2K/US - this.Add(VirtualKeys.OEM_7, Key.Quote); // Varies by keyboard, '" on Win2K/US - this.Add(VirtualKeys.OEM_PLUS, Key.Plus); // Invariant: + - this.Add(VirtualKeys.OEM_COMMA, Key.Comma); // Invariant: , - this.Add(VirtualKeys.OEM_MINUS, Key.Minus); // Invariant: - - this.Add(VirtualKeys.OEM_PERIOD, Key.Period); // Invariant: . - - this.Add(VirtualKeys.HOME, Key.Home); - this.Add(VirtualKeys.END, Key.End); - this.Add(VirtualKeys.DELETE, Key.Delete); - this.Add(VirtualKeys.PRIOR, Key.PageUp); - this.Add(VirtualKeys.NEXT, Key.PageDown); - this.Add(VirtualKeys.PRINT, Key.PrintScreen); - this.Add(VirtualKeys.PAUSE, Key.Pause); - this.Add(VirtualKeys.NUMLOCK, Key.NumLock); - - this.Add(VirtualKeys.SCROLL, Key.ScrollLock); - this.Add(VirtualKeys.SNAPSHOT, Key.PrintScreen); - this.Add(VirtualKeys.CLEAR, Key.Clear); - this.Add(VirtualKeys.INSERT, Key.Insert); - - this.Add(VirtualKeys.SLEEP, Key.Sleep); - - // Keypad - for (int i = 0; i <= 9; i++) - { - this.Add((VirtualKeys)((int)VirtualKeys.NUMPAD0 + i), Key.Keypad0 + i); - } - this.Add(VirtualKeys.DECIMAL, Key.KeypadDecimal); - this.Add(VirtualKeys.ADD, Key.KeypadAdd); - this.Add(VirtualKeys.SUBTRACT, Key.KeypadSubtract); - this.Add(VirtualKeys.DIVIDE, Key.KeypadDivide); - this.Add(VirtualKeys.MULTIPLY, Key.KeypadMultiply); - - // Navigation - this.Add(VirtualKeys.UP, Key.Up); - this.Add(VirtualKeys.DOWN, Key.Down); - this.Add(VirtualKeys.LEFT, Key.Left); - this.Add(VirtualKeys.RIGHT, Key.Right); + this.Add((VirtualKeys)((int)VirtualKeys.F1 + i), Key.F1 + i); } - catch (ArgumentException e) + + // Number keys (0-9) + for (int i = 0; i <= 9; i++) { - Debug.Print("Exception while creating keymap: '{0}'.", e.ToString()); - System.Windows.Forms.MessageBox.Show( - String.Format("Exception while creating keymap: '{0}'.", e.ToString())); + this.Add((VirtualKeys)(0x30 + i), Key.Number0 + i); } + + // Letters (A-Z) + for (int i = 0; i < 26; i++) + { + this.Add((VirtualKeys)(0x41 + i), Key.A + i); + } + + this.Add(VirtualKeys.TAB, Key.Tab); + this.Add(VirtualKeys.CAPITAL, Key.CapsLock); + this.Add(VirtualKeys.LCONTROL, Key.ControlLeft); + this.Add(VirtualKeys.LSHIFT, Key.ShiftLeft); + this.Add(VirtualKeys.LWIN, Key.WinLeft); + this.Add(VirtualKeys.LMENU, Key.AltLeft); + this.Add(VirtualKeys.SPACE, Key.Space); + this.Add(VirtualKeys.RMENU, Key.AltRight); + this.Add(VirtualKeys.RWIN, Key.WinRight); + this.Add(VirtualKeys.APPS, Key.Menu); + this.Add(VirtualKeys.RCONTROL, Key.ControlRight); + this.Add(VirtualKeys.RSHIFT, Key.ShiftRight); + this.Add(VirtualKeys.RETURN, Key.Enter); + this.Add(VirtualKeys.BACK, Key.BackSpace); + + this.Add(VirtualKeys.OEM_1, Key.Semicolon); // Varies by keyboard, ;: on Win2K/US + this.Add(VirtualKeys.OEM_2, Key.Slash); // Varies by keyboard, /? on Win2K/US + this.Add(VirtualKeys.OEM_3, Key.Tilde); // Varies by keyboard, `~ on Win2K/US + this.Add(VirtualKeys.OEM_4, Key.BracketLeft); // Varies by keyboard, [{ on Win2K/US + this.Add(VirtualKeys.OEM_5, Key.BackSlash); // Varies by keyboard, \| on Win2K/US + this.Add(VirtualKeys.OEM_6, Key.BracketRight); // Varies by keyboard, ]} on Win2K/US + this.Add(VirtualKeys.OEM_7, Key.Quote); // Varies by keyboard, '" on Win2K/US + this.Add(VirtualKeys.OEM_PLUS, Key.Plus); // Invariant: + + this.Add(VirtualKeys.OEM_COMMA, Key.Comma); // Invariant: , + this.Add(VirtualKeys.OEM_MINUS, Key.Minus); // Invariant: - + this.Add(VirtualKeys.OEM_PERIOD, Key.Period); // Invariant: . + + this.Add(VirtualKeys.HOME, Key.Home); + this.Add(VirtualKeys.END, Key.End); + this.Add(VirtualKeys.DELETE, Key.Delete); + this.Add(VirtualKeys.PRIOR, Key.PageUp); + this.Add(VirtualKeys.NEXT, Key.PageDown); + this.Add(VirtualKeys.PRINT, Key.PrintScreen); + this.Add(VirtualKeys.PAUSE, Key.Pause); + this.Add(VirtualKeys.NUMLOCK, Key.NumLock); + + this.Add(VirtualKeys.SCROLL, Key.ScrollLock); + this.Add(VirtualKeys.SNAPSHOT, Key.PrintScreen); + this.Add(VirtualKeys.CLEAR, Key.Clear); + this.Add(VirtualKeys.INSERT, Key.Insert); + + this.Add(VirtualKeys.SLEEP, Key.Sleep); + + // Keypad + for (int i = 0; i <= 9; i++) + { + this.Add((VirtualKeys)((int)VirtualKeys.NUMPAD0 + i), Key.Keypad0 + i); + } + this.Add(VirtualKeys.DECIMAL, Key.KeypadDecimal); + this.Add(VirtualKeys.ADD, Key.KeypadAdd); + this.Add(VirtualKeys.SUBTRACT, Key.KeypadSubtract); + this.Add(VirtualKeys.DIVIDE, Key.KeypadDivide); + this.Add(VirtualKeys.MULTIPLY, Key.KeypadMultiply); + + // Navigation + this.Add(VirtualKeys.UP, Key.Up); + this.Add(VirtualKeys.DOWN, Key.Down); + this.Add(VirtualKeys.LEFT, Key.Left); + this.Add(VirtualKeys.RIGHT, Key.Right); } } } diff --git a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs b/Source/OpenTK/Platform/Windows/WinMMJoystick.cs index 497ea179..ea3c2a5e 100644 --- a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs +++ b/Source/OpenTK/Platform/Windows/WinMMJoystick.cs @@ -36,7 +36,7 @@ using System.Diagnostics; namespace OpenTK.Platform.Windows { - sealed class WinMMJoystick : IJoystickDriver + sealed class WinMMJoystick : IJoystickDriver, IGamePadDriver { #region Fields diff --git a/Source/OpenTK/Platform/Windows/WinRawInput.cs b/Source/OpenTK/Platform/Windows/WinRawInput.cs index bd815741..54425ded 100644 --- a/Source/OpenTK/Platform/Windows/WinRawInput.cs +++ b/Source/OpenTK/Platform/Windows/WinRawInput.cs @@ -1,60 +1,169 @@ -#region --- License --- -/* Copyright (c) 2006, 2007 Stefanos Apostolopoulos - * See license.txt for license info - */ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 --- Using directives --- - using System; -using System.Collections.Generic; -using System.Text; -using System.Runtime.InteropServices; using System.Diagnostics; -using System.Windows.Forms; +using System.Runtime.InteropServices; +using System.Threading; using OpenTK.Input; -#endregion - namespace OpenTK.Platform.Windows { - // Not complete. - sealed class WinRawInput : System.Windows.Forms.NativeWindow, IInputDriver + sealed class WinRawInput : WinInputBase { + #region Fields + // Input event data. - RawInput data = new RawInput(); - // The total number of input devices connected to this system. - static int deviceCount; - int rawInputStructSize = API.RawInputSize; + static RawInput data = new RawInput(); + static readonly int rawInputStructSize = API.RawInputSize; - private WinRawKeyboard keyboardDriver; - private WinRawMouse mouseDriver; + WinRawKeyboard keyboard_driver; + WinRawMouse mouse_driver; + WinMMJoystick joystick_driver; - #region --- Constructors --- + IntPtr DevNotifyHandle; + static readonly Guid DeviceInterfaceHid = new Guid("4D1E55B2-F16F-11CF-88CB-001111000030"); - internal WinRawInput(WinWindowInfo parent) + #endregion + + #region Constructors + + public WinRawInput() + : base() { - Debug.WriteLine("Initalizing windows raw input driver."); - Debug.Indent(); - - AssignHandle(parent.WindowHandle); - Debug.Print("Input window attached to parent {0}", parent); - keyboardDriver = new WinRawKeyboard(this.Handle); - mouseDriver = new WinRawMouse(this.Handle); - - Debug.Unindent(); - - //AllocateBuffer(); + Debug.WriteLine("Using WinRawInput."); } #endregion - #region internal static int DeviceCount + #region Private Members - internal static int DeviceCount + static IntPtr RegisterForDeviceNotifications(WinWindowInfo parent) + { + IntPtr dev_notify_handle; + BroadcastDeviceInterface bdi = new BroadcastDeviceInterface(); + bdi.Size = BlittableValueType.StrideOf(bdi); + bdi.DeviceType = DeviceBroadcastType.INTERFACE; + bdi.ClassGuid = DeviceInterfaceHid; + unsafe + { + dev_notify_handle = Functions.RegisterDeviceNotification(parent.WindowHandle, + new IntPtr((void*)&bdi), DeviceNotification.WINDOW_HANDLE); + } + if (dev_notify_handle == IntPtr.Zero) + Debug.Print("[Warning] Failed to register for device notifications. Error: {0}", Marshal.GetLastWin32Error()); + + return dev_notify_handle; + } + + + #endregion + + #region Protected Members + + #region WindowProcedure + + // Processes the input Windows Message, routing the buffer to the correct Keyboard, Mouse or HID. + protected override IntPtr WindowProcedure( + IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + switch (message) + { + case WindowMessage.INPUT: + int size = 0; + // Get the size of the input buffer + Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT, + IntPtr.Zero, ref size, API.RawInputHeaderSize); + + // Read the actual raw input structure + if (size == Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT, + out data, ref size, API.RawInputHeaderSize)) + { + switch (data.Header.Type) + { + case RawInputDeviceType.KEYBOARD: + if (((WinRawKeyboard)KeyboardDriver).ProcessKeyboardEvent(data)) + return IntPtr.Zero; + break; + + case RawInputDeviceType.MOUSE: + if (((WinRawMouse)MouseDriver).ProcessMouseEvent(data)) + return IntPtr.Zero; + break; + + case RawInputDeviceType.HID: + break; + } + } + break; + + case WindowMessage.DEVICECHANGE: + ((WinRawKeyboard)KeyboardDriver).RefreshDevices(); + ((WinRawMouse)MouseDriver).RefreshDevices(); + break; + } + return base.WindowProcedure(handle, message, wParam, lParam); + } + + #endregion + + #region CreateDrivers + + protected override void CreateDrivers() + { + keyboard_driver = new WinRawKeyboard(Parent.WindowHandle); + mouse_driver = new WinRawMouse(Parent.WindowHandle); + joystick_driver = new WinMMJoystick(); + + DevNotifyHandle = RegisterForDeviceNotifications(Parent); + } + + #endregion + + protected override void Dispose(bool manual) + { + if (!Disposed) + { + Functions.UnregisterDeviceNotification(DevNotifyHandle); + base.Dispose(manual); + } + } + + #endregion + + #region Public Members + + #region DeviceCount + + public static int DeviceCount { get { + int deviceCount = 0; Functions.GetRawInputDeviceList(null, ref deviceCount, API.RawInputDeviceListSize); return deviceCount; } @@ -62,200 +171,23 @@ namespace OpenTK.Platform.Windows #endregion - #region protected override void WndProc(ref Message msg) - - /// - /// Processes the input Windows Message, routing the buffer to the correct Keyboard, Mouse or HID. - /// - /// The WM_INPUT message, containing the buffer on the input event. - protected override void WndProc(ref Message msg) - { - switch ((WindowMessage)msg.Msg) - { - case WindowMessage.INPUT: - int size = 0; - // Get the size of the input buffer - Functions.GetRawInputData(msg.LParam, GetRawInputDataEnum.INPUT, - IntPtr.Zero, ref size, API.RawInputHeaderSize); - - //if (buffer == null || API.RawInputSize < size) - //{ - // throw new ApplicationException("Critical error when processing raw windows input."); - //} - if (size == Functions.GetRawInputData(msg.LParam, GetRawInputDataEnum.INPUT, - out data, ref size, API.RawInputHeaderSize)) - { - switch (data.Header.Type) - { - case RawInputDeviceType.KEYBOARD: - if (!keyboardDriver.ProcessKeyboardEvent(data)) - Functions.DefRawInputProc(ref data, 1, (uint)API.RawInputHeaderSize); - return; - - case RawInputDeviceType.MOUSE: - if (!mouseDriver.ProcessEvent(data)) - Functions.DefRawInputProc(ref data, 1, (uint)API.RawInputHeaderSize); - return; - - case RawInputDeviceType.HID: - Functions.DefRawInputProc(ref data, 1, (uint)API.RawInputHeaderSize); - return; - - default: - break; - } - } - else - { - throw new ApplicationException(String.Format( - "GetRawInputData returned invalid buffer. Windows error {0}. Please file a bug at http://opentk.sourceforge.net", - Marshal.GetLastWin32Error())); - } - break; - - case WindowMessage.DESTROY: - Debug.Print("Input window detached from parent {0}.", Handle); - ReleaseHandle(); - break; - - case WindowMessage.QUIT: - Debug.WriteLine("Input window quit."); - this.Dispose(); - break; - } - - base.WndProc(ref msg); - } - #endregion - #region --- IInputDriver Members --- + #region IInputDriver2 Members - #region IInputDriver Members - - public void Poll() + public override IKeyboardDriver2 KeyboardDriver { - return; -#if false - // We will do a buffered read for all input devices and route the RawInput structures - // to the correct 'ProcessData' handlers. First, we need to find out the size of the - // buffer to allocate for the structures. Then we allocate the buffer and read the - // structures, calling the correct handler for each one. Last, we free the allocated - // buffer. - int size = 0; - Functions.GetRawInputBuffer(IntPtr.Zero, ref size, API.RawInputHeaderSize); - size *= 256; - IntPtr rin_data = Marshal.AllocHGlobal(size); - - while (true) - { - // Iterate reading all available RawInput structures and routing them to their respective - // handlers. - int num = Functions.GetRawInputBuffer(rin_data, ref size, API.RawInputHeaderSize); - if (num == 0) - break; - else if (num < 0) - { - /*int error = Marshal.GetLastWin32Error(); - if (error == 122) - { - // Enlarge the buffer, it was too small. - AllocateBuffer(); - } - else - { - throw new ApplicationException(String.Format( - "GetRawInputBuffer failed with code: {0}", error)); - }*/ - Debug.Print("GetRawInputBuffer failed with code: {0}", Marshal.GetLastWin32Error()); - //AllocateBuffer(); - break; - } - - RawInput[] rin_structs = new RawInput[num]; - IntPtr next_rin = rin_data; - for (int i = 0; i < num; i++) - { - rin_structs[i] = (RawInput)Marshal.PtrToStructure(next_rin, typeof(RawInput)); - - switch (rin_structs[i].Header.Type) - { - case RawInputDeviceType.KEYBOARD: - keyboardDriver.ProcessKeyboardEvent(rin_structs[i]); - break; - - case RawInputDeviceType.MOUSE: - mouseDriver.ProcessEvent(rin_structs[i]); - break; - } - - next_rin = Functions.NextRawInputStructure(next_rin); - } - Functions.DefRawInputProc(rin_structs, num, (uint)API.RawInputHeaderSize); - } - - Marshal.FreeHGlobal(rin_data); -#endif + get { return keyboard_driver; } } - #endregion - - #region IKeyboardDriver Members - - public IList Keyboard + public override IMouseDriver2 MouseDriver { - get { return keyboardDriver.Keyboard; } + get { return mouse_driver; } } - #endregion - - #region IMouseDriver Members - - public IList Mouse + public override IGamePadDriver GamePadDriver { - get { return mouseDriver.Mouse; } - } - - #endregion - - #region IJoystickDriver Members - - public IList Joysticks - { - get { throw new NotImplementedException(); } - } - - #endregion - - #endregion - - #region --- IDisposable Members --- - - private bool disposed; - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool manual) - { - if (!disposed) - { - if (manual) - { - keyboardDriver.Dispose(); - this.ReleaseHandle(); - } - - disposed = true; - } - } - - ~WinRawInput() - { - Dispose(false); + get { return joystick_driver; } } #endregion diff --git a/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs b/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs index 4bf8b994..9b2a1989 100644 --- a/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs +++ b/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs @@ -1,130 +1,247 @@ -#region --- License --- -/* Copyright (c) 2007 Stefanos Apostolopoulos - * See license.txt for license info - */ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 --- Using directives --- - using System; using System.Collections.Generic; -using System.Text; -using System.Runtime.InteropServices; using System.Diagnostics; +using System.Runtime.InteropServices; using Microsoft.Win32; using OpenTK.Input; -#endregion - namespace OpenTK.Platform.Windows { - internal class WinRawKeyboard : IKeyboardDriver, IDisposable + sealed class WinRawKeyboard : IKeyboardDriver2 { - private List keyboards = new List(); - private IntPtr window; + static readonly WinKeyMap KeyMap = new WinKeyMap(); + readonly List keyboards = new List(); + readonly List names = new List(); + readonly Dictionary rawids = new Dictionary(); + readonly IntPtr window; + readonly object UpdateLock = new object(); - #region --- Constructors --- + #region Constructors - internal WinRawKeyboard() - : this(IntPtr.Zero) + public WinRawKeyboard(IntPtr windowHandle) { - } - - internal WinRawKeyboard(IntPtr windowHandle) - { - Debug.WriteLine("Initializing keyboard driver (WinRawKeyboard)."); + Debug.WriteLine("Using WinRawKeyboard."); Debug.Indent(); this.window = windowHandle; - - UpdateKeyboardList(); + RefreshDevices(); Debug.Unindent(); } #endregion - #region internal static void UpdateKeyboardList() + #region Public Members - internal void UpdateKeyboardList() + public void RefreshDevices() { - int count = WinRawInput.DeviceCount; - RawInputDeviceList[] ridl = new RawInputDeviceList[count]; - for (int i = 0; i < count; i++) - ridl[i] = new RawInputDeviceList(); - Functions.GetRawInputDeviceList(ridl, ref count, API.RawInputDeviceListSize); - - // Discover keyboard devices: - for (int i = 0; i < count; i++) + lock (UpdateLock) { - uint size = 0; - Functions.GetRawInputDeviceInfo(ridl[i].Device, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size); - IntPtr name_ptr = Marshal.AllocHGlobal((IntPtr)size); - Functions.GetRawInputDeviceInfo(ridl[i].Device, RawInputDeviceInfoEnum.DEVICENAME, name_ptr, ref size); - string name = Marshal.PtrToStringAnsi(name_ptr); - Marshal.FreeHGlobal(name_ptr); - if (name.ToLower().Contains("root")) + for (int i = 0; i < keyboards.Count; i++) { - // This is a terminal services device, skip it. - continue; + KeyboardState state = keyboards[i]; + state.IsConnected = false; + keyboards[i] = state; } - else if (ridl[i].Type == RawInputDeviceType.KEYBOARD || ridl[i].Type == RawInputDeviceType.HID) + + int count = WinRawInput.DeviceCount; + RawInputDeviceList[] ridl = new RawInputDeviceList[count]; + for (int i = 0; i < count; i++) + ridl[i] = new RawInputDeviceList(); + Functions.GetRawInputDeviceList(ridl, ref count, API.RawInputDeviceListSize); + + // Discover keyboard devices: + foreach (RawInputDeviceList dev in ridl) { - // This is a keyboard or USB keyboard device. In the latter case, discover if it really is a - // keyboard device by qeurying the registry. - - // remove the \??\ - name = name.Substring(4); - - string[] split = name.Split('#'); - - string id_01 = split[0]; // ACPI (Class code) - string id_02 = split[1]; // PNP0303 (SubClass code) - string id_03 = split[2]; // 3&13c0b0c5&0 (Protocol code) - // The final part is the class GUID and is not needed here - - string findme = string.Format( - @"System\CurrentControlSet\Enum\{0}\{1}\{2}", - id_01, id_02, id_03); - - RegistryKey regkey = Registry.LocalMachine.OpenSubKey(findme); - - string deviceDesc = - (string)regkey.GetValue("DeviceDesc"); - string deviceClass = - (string)regkey.GetValue("Class"); - if (!String.IsNullOrEmpty(deviceClass) && deviceClass.ToLower().Equals("keyboard")) + ContextHandle id = new ContextHandle(dev.Device); + if (rawids.ContainsKey(id)) { - KeyboardDevice kb = new KeyboardDevice(); - kb.Description = deviceDesc; + // Device already registered, mark as connected + KeyboardState state = keyboards[rawids[id]]; + state.IsConnected = true; + keyboards[rawids[id]] = state; + continue; + } - // Register the keyboard: - RawInputDeviceInfo info = new RawInputDeviceInfo(); - int devInfoSize = API.RawInputDeviceInfoSize; - Functions.GetRawInputDeviceInfo(ridl[i].Device, RawInputDeviceInfoEnum.DEVICEINFO, - info, ref devInfoSize); + string name = GetDeviceName(dev); + if (name.ToLower().Contains("root")) + { + // This is a terminal services device, skip it. + continue; + } + else if (dev.Type == RawInputDeviceType.KEYBOARD || dev.Type == RawInputDeviceType.HID) + { + // 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"); + string deviceClass = (string)regkey.GetValue("Class"); - kb.NumberOfLeds = info.Device.Keyboard.NumberOfIndicators; - kb.NumberOfFunctionKeys = info.Device.Keyboard.NumberOfFunctionKeys; - kb.NumberOfKeys = info.Device.Keyboard.NumberOfKeysTotal; - //kb.DeviceID = (info.Device.Keyboard.Type << 32) + info.Device.Keyboard.SubType; - kb.DeviceID = ridl[i].Device; + if (String.IsNullOrEmpty(deviceDesc)) + { + Debug.Print("[Warning] Failed to retrieve device description, skipping this device."); + continue; + } + else + { + deviceDesc = deviceDesc.Substring(deviceDesc.LastIndexOf(';') + 1); + } - //if (!keyboards.Contains(kb)) - //{ - this.RegisterKeyboardDevice(kb); - keyboards.Add(kb); - //} + if (!String.IsNullOrEmpty(deviceClass) && deviceClass.ToLower().Equals("keyboard")) + { + // Register the keyboard: + RawInputDeviceInfo info = new RawInputDeviceInfo(); + int devInfoSize = API.RawInputDeviceInfoSize; + Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICEINFO, + info, ref devInfoSize); + + //KeyboardDevice kb = new KeyboardDevice(); + //kb.Description = deviceDesc; + //kb.NumberOfLeds = info.Device.Keyboard.NumberOfIndicators; + //kb.NumberOfFunctionKeys = info.Device.Keyboard.NumberOfFunctionKeys; + //kb.NumberOfKeys = info.Device.Keyboard.NumberOfKeysTotal; + //kb.DeviceID = dev.Device; + + RegisterKeyboardDevice(window, deviceDesc); + KeyboardState state = new KeyboardState(); + state.IsConnected = true; + keyboards.Add(state); + names.Add(deviceDesc); + rawids.Add(new ContextHandle(dev.Device), keyboards.Count - 1); + } } } } } + public bool ProcessKeyboardEvent(RawInput rin) + { + bool processed = false; + + bool pressed = + rin.Data.Keyboard.Message == (int)WindowMessage.KEYDOWN || + rin.Data.Keyboard.Message == (int)WindowMessage.SYSKEYDOWN; + + ContextHandle handle = new ContextHandle(rin.Header.Device); + KeyboardState keyboard; + if (!rawids.ContainsKey(handle)) + { + RefreshDevices(); + } + + if (keyboards.Count == 0) + return false; + + // Note:For some reason, my Microsoft Digital 3000 keyboard reports 0 + // as rin.Header.Device for the "zoom-in/zoom-out" buttons. + // That's problematic, because no device has a "0" id. + // As a workaround, we'll add those buttons to the first device (if any). + int keyboard_handle = rawids.ContainsKey(handle) ? rawids[handle] : 0; + keyboard = keyboards[keyboard_handle]; + + // Generic control, shift, alt keys may be sent instead of left/right. + // It seems you have to explicitly register left/right events. + switch (rin.Data.Keyboard.VKey) + { + case VirtualKeys.SHIFT: + keyboard[Input.Key.ShiftLeft] = keyboard[Input.Key.ShiftRight] = pressed; + processed = true; + break; + + case VirtualKeys.CONTROL: + keyboard[Input.Key.ControlLeft] = keyboard[Input.Key.ControlRight] = pressed; + processed = true; + break; + + case VirtualKeys.MENU: + keyboard[Input.Key.AltLeft] = keyboard[Input.Key.AltRight] = pressed; + processed = true; + break; + + default: + if (!KeyMap.ContainsKey(rin.Data.Keyboard.VKey)) + { + Debug.Print("Virtual key {0} ({1}) not mapped.", + rin.Data.Keyboard.VKey, (int)rin.Data.Keyboard.VKey); + } + else + { + keyboard[KeyMap[rin.Data.Keyboard.VKey]] = pressed; + processed = true; + } + break; + } + + lock (UpdateLock) + { + keyboards[keyboard_handle] = keyboard; + return processed; + } + } + #endregion - #region internal void RegisterKeyboardDevice(Keyboard kb) + #region Private Members - internal void RegisterKeyboardDevice(KeyboardDevice kb) + static RegistryKey GetRegistryKey(string name) + { + // remove the \??\ + name = name.Substring(4); + + string[] split = name.Split('#'); + + string id_01 = split[0]; // ACPI (Class code) + string id_02 = split[1]; // PNP0303 (SubClass code) + string id_03 = split[2]; // 3&13c0b0c5&0 (Protocol code) + // The final part is the class GUID and is not needed here + + string findme = string.Format( + @"System\CurrentControlSet\Enum\{0}\{1}\{2}", + id_01, id_02, id_03); + + RegistryKey regkey = Registry.LocalMachine.OpenSubKey(findme); + return regkey; + } + + static string GetDeviceName(RawInputDeviceList dev) + { + uint size = 0; + Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size); + IntPtr name_ptr = Marshal.AllocHGlobal((IntPtr)size); + Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, name_ptr, ref size); + string name = Marshal.PtrToStringAnsi(name_ptr); + Marshal.FreeHGlobal(name_ptr); + return name; + } + + static void RegisterKeyboardDevice(IntPtr window, string name) { RawInputDevice[] rid = new RawInputDevice[1]; // Keyboard is 1/6 (page/id). See http://www.microsoft.com/whdc/device/input/HID_HWID.mspx @@ -136,134 +253,52 @@ namespace OpenTK.Platform.Windows if (!Functions.RegisterRawInputDevices(rid, 1, API.RawInputDeviceSize)) { - throw new ApplicationException( - String.Format( - "Raw input registration failed with error: {0}. Device: {1}", - Marshal.GetLastWin32Error(), - rid[0].ToString()) - ); + Debug.Print("[Warning] Raw input registration failed with error: {0}. Device: {1}", + Marshal.GetLastWin32Error(), rid[0].ToString()); } else { - Debug.Print("Registered keyboard {0}", kb.ToString()); + Debug.Print("Registered keyboard {0}", name); } } #endregion - #region internal bool ProcessKeyboardEvent(API.RawInput rin) + #region IKeyboardDriver2 Members - /// - /// Processes raw input events. - /// - /// - /// - internal bool ProcessKeyboardEvent(RawInput rin) + public KeyboardState GetState() { - //Keyboard key = keyboards[0]; - //rin.Header.Device; - switch (rin.Header.Type) + lock (UpdateLock) { - case RawInputDeviceType.KEYBOARD: - bool pressed = - rin.Data.Keyboard.Message == (int)WindowMessage.KEYDOWN || - rin.Data.Keyboard.Message == (int)WindowMessage.SYSKEYDOWN; - - // Find the device where the button was pressed. It can be that the input notification - // came not from a physical keyboard device but from a code-generated input message - in - // that case, the event goes to the default (first) keyboard. - // TODO: Send the event to all keyboards instead of the default one. - // TODO: Optimize this! The predicate allocates way to much memory. - //int index = keyboards.FindIndex(delegate(KeyboardDevice kb) - //{ - // return kb.DeviceID == rin.Header.Device; - //}); - //if (index == -1) index = 0; - int index; - if (keyboards.Count > 0) index = 0; - else return false; - - // Generic control, shift, alt keys may be sent instead of left/right. - // It seems you have to explicitly register left/right events. - switch (rin.Data.Keyboard.VKey) - { - case VirtualKeys.SHIFT: - keyboards[index][Input.Key.ShiftLeft] = keyboards[index][Input.Key.ShiftRight] = pressed; - return true; - - case VirtualKeys.CONTROL: - keyboards[index][Input.Key.ControlLeft] = keyboards[index][Input.Key.ControlRight] = pressed; - return true; - - case VirtualKeys.MENU: - keyboards[index][Input.Key.AltLeft] = keyboards[index][Input.Key.AltRight] = pressed; - return true; - - default: - if (!WMInput.KeyMap.ContainsKey(rin.Data.Keyboard.VKey)) - Debug.Print("Virtual key {0} ({1}) not mapped.", - rin.Data.Keyboard.VKey, (int)rin.Data.Keyboard.VKey); - else - keyboards[index][WMInput.KeyMap[rin.Data.Keyboard.VKey]] = pressed; - - return false; - } - - default: - throw new ApplicationException("Windows raw keyboard driver received invalid data."); - } - } - - #endregion - - #region --- IInputDevice Members --- - - public string Description - { - get { throw new Exception("The method or operation is not implemented."); } - } - - public Input.InputDeviceType DeviceType - { - get { return Input.InputDeviceType.Keyboard; } - } - - #endregion - - #region --- IKeyboardDriver Members --- - - public IList Keyboard - { - get { return keyboards; } - } - - #endregion - - #region --- IDisposable Members --- - - private bool disposed; - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool manual) - { - if (!disposed) - { - if (manual) + KeyboardState master = new KeyboardState(); + foreach (KeyboardState ks in keyboards) { - keyboards.Clear(); + master.MergeBits(ks); } - disposed = true; + return master; } } - ~WinRawKeyboard() + public KeyboardState GetState(int index) { - Dispose(false); + lock (UpdateLock) + { + if (keyboards.Count > index) + return keyboards[index]; + else + return new KeyboardState(); + } + } + + public string GetDeviceName(int index) + { + lock (UpdateLock) + { + if (names.Count > index) + return names[index]; + else + return String.Empty; + } } #endregion diff --git a/Source/OpenTK/Platform/Windows/WinRawMouse.cs b/Source/OpenTK/Platform/Windows/WinRawMouse.cs index cfaf2c78..38e8a371 100644 --- a/Source/OpenTK/Platform/Windows/WinRawMouse.cs +++ b/Source/OpenTK/Platform/Windows/WinRawMouse.cs @@ -1,17 +1,36 @@ -#region --- License --- -/* Copyright (c) 2006, 2007 Stefanos Apostolopoulos - * See license.txt for license info - */ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 using System; using System.Collections.Generic; -using System.Text; -using OpenTK.Input; using System.Diagnostics; using System.Runtime.InteropServices; using Microsoft.Win32; -using System.Drawing; +using OpenTK.Input; namespace OpenTK.Platform.Windows { @@ -19,121 +38,198 @@ namespace OpenTK.Platform.Windows /// /// Contains methods to register for and process mouse WM_INPUT messages. /// - internal class WinRawMouse : IMouseDriver, IDisposable + sealed class WinRawMouse : IMouseDriver2 { - private List mice = new List(); - private IntPtr window; + readonly List mice = new List(); + readonly List names = new List(); + readonly Dictionary rawids = new Dictionary(); + readonly IntPtr Window; + readonly object UpdateLock = new object(); - #region --- Constructors --- + #region Constructors - internal WinRawMouse() - : this(IntPtr.Zero) + public WinRawMouse(IntPtr window) { - } - - internal WinRawMouse(IntPtr windowHandle) - { - Debug.WriteLine("Initializing mouse driver (WinRawMouse)."); + Debug.WriteLine("Using WinRawMouse."); Debug.Indent(); - this.window = windowHandle; + if (window == IntPtr.Zero) + throw new ArgumentNullException("window"); - RegisterDevices(); + Window = window; + RefreshDevices(); Debug.Unindent(); } #endregion - #region --- IMouseDriver Members --- + #region Public Members - public IList Mouse + public void RefreshDevices() { - get { return mice; } - } - - #region public int RegisterDevices() - - public int RegisterDevices() - { - int count = WinRawInput.DeviceCount; - RawInputDeviceList[] ridl = new RawInputDeviceList[count]; - for (int i = 0; i < count; i++) - ridl[i] = new RawInputDeviceList(); - Functions.GetRawInputDeviceList(ridl, ref count, API.RawInputDeviceListSize); - - // Discover mouse devices: - for (int i = 0; i < count; i++) + lock (UpdateLock) { - uint size = 0; - Functions.GetRawInputDeviceInfo(ridl[i].Device, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size); - IntPtr name_ptr = Marshal.AllocHGlobal((IntPtr)size); - Functions.GetRawInputDeviceInfo(ridl[i].Device, RawInputDeviceInfoEnum.DEVICENAME, name_ptr, ref size); - string name = Marshal.PtrToStringAnsi(name_ptr); - Marshal.FreeHGlobal(name_ptr); - - if (name.ToLower().Contains("root")) + // Mark all devices as disconnected. We will check which of those + // are connected later on. + for (int i = 0; i < mice.Count; i++) { - // This is a terminal services device, skip it. - continue; + MouseState state = mice[i]; + state.IsConnected = false; + mice[i] = state; } - else if (ridl[i].Type == RawInputDeviceType.MOUSE || ridl[i].Type == RawInputDeviceType.HID) + + int count = WinRawInput.DeviceCount; + RawInputDeviceList[] ridl = new RawInputDeviceList[count]; + for (int i = 0; i < count; i++) + ridl[i] = new RawInputDeviceList(); + Functions.GetRawInputDeviceList(ridl, ref count, API.RawInputDeviceListSize); + + // Discover mouse devices + foreach (RawInputDeviceList dev in ridl) { - // 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. - - // remove the \??\ - name = name.Substring(4); - - string[] split = name.Split('#'); - - string id_01 = split[0]; // ACPI (Class code) - string id_02 = split[1]; // PNP0303 (SubClass code) - string id_03 = split[2]; // 3&13c0b0c5&0 (Protocol code) - // The final part is the class GUID and is not needed here - - string findme = string.Format( - @"System\CurrentControlSet\Enum\{0}\{1}\{2}", - id_01, id_02, id_03); - - RegistryKey regkey = Registry.LocalMachine.OpenSubKey(findme); - - string deviceDesc = (string)regkey.GetValue("DeviceDesc"); - deviceDesc = deviceDesc.Substring(deviceDesc.LastIndexOf(';') + 1); - string deviceClass = (string)regkey.GetValue("Class"); - - if (!String.IsNullOrEmpty(deviceClass) && deviceClass.ToLower().Equals("mouse")) + ContextHandle id = new ContextHandle(dev.Device); + if (rawids.ContainsKey(id)) { - OpenTK.Input.MouseDevice mouse = new OpenTK.Input.MouseDevice(); - mouse.Description = deviceDesc; + // Device already registered, mark as connected + MouseState state = mice[rawids[id]]; + state.IsConnected = true; + mice[rawids[id]] = state; + continue; + } - // Register the keyboard: - RawInputDeviceInfo info = new RawInputDeviceInfo(); - int devInfoSize = API.RawInputDeviceInfoSize; - Functions.GetRawInputDeviceInfo(ridl[i].Device, RawInputDeviceInfoEnum.DEVICEINFO, - info, ref devInfoSize); + // Unregistered device, find what it is + string name = GetDeviceName(dev); + if (name.ToLower().Contains("root")) + { + // This is a terminal services device, skip it. + continue; + } + else if (dev.Type == RawInputDeviceType.MOUSE || dev.Type == RawInputDeviceType.HID) + { + // 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"); + string deviceClass = (string)regkey.GetValue("Class"); + deviceDesc = deviceDesc.Substring(deviceDesc.LastIndexOf(';') + 1); - mouse.NumberOfButtons = info.Device.Mouse.NumberOfButtons; - mouse.NumberOfWheels = info.Device.Mouse.HasHorizontalWheel ? 1 : 0; + if (!String.IsNullOrEmpty(deviceClass) && deviceClass.ToLower().Equals("mouse")) + { + if (!rawids.ContainsKey(new ContextHandle(dev.Device))) + { + // Register the device: + RawInputDeviceInfo info = new RawInputDeviceInfo(); + int devInfoSize = API.RawInputDeviceInfoSize; + Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICEINFO, + info, ref devInfoSize); - mouse.DeviceID = ridl[i].Device;//(IntPtr)info.Device.Mouse.Id; - - this.RegisterRawDevice(mouse); - mice.Add(mouse); + RegisterRawDevice(Window, deviceDesc); + MouseState state = new MouseState(); + state.IsConnected = true; + mice.Add(state); + names.Add(deviceDesc); + rawids.Add(new ContextHandle(dev.Device), mice.Count - 1); + } + } } } } + } - return count; + public bool ProcessMouseEvent(RawInput rin) + { + RawMouse raw = rin.Data.Mouse; + ContextHandle handle = new ContextHandle(rin.Header.Device); + + MouseState mouse; + if (!rawids.ContainsKey(handle)) + { + RefreshDevices(); + } + + if (mice.Count == 0) + return false; + + // Note:For some reason, my Microsoft Digital 3000 keyboard reports 0 + // as rin.Header.Device for the "zoom-in/zoom-out" buttons. + // That's problematic, because no device has a "0" id. + // As a workaround, we'll add those buttons to the first device (if any). + int mouse_handle = rawids.ContainsKey(handle) ? rawids[handle] : 0; + mouse = mice[mouse_handle]; + + if ((raw.ButtonFlags & RawInputMouseState.LEFT_BUTTON_DOWN) != 0) mouse.EnableBit((int)MouseButton.Left); + if ((raw.ButtonFlags & RawInputMouseState.LEFT_BUTTON_UP) != 0) mouse.DisableBit((int)MouseButton.Left); + if ((raw.ButtonFlags & RawInputMouseState.RIGHT_BUTTON_DOWN) != 0) mouse.EnableBit((int)MouseButton.Right); + if ((raw.ButtonFlags & RawInputMouseState.RIGHT_BUTTON_UP) != 0) mouse.DisableBit((int)MouseButton.Right); + if ((raw.ButtonFlags & RawInputMouseState.MIDDLE_BUTTON_DOWN) != 0) mouse.EnableBit((int)MouseButton.Middle); + if ((raw.ButtonFlags & RawInputMouseState.MIDDLE_BUTTON_UP) != 0) mouse.DisableBit((int)MouseButton.Middle); + if ((raw.ButtonFlags & RawInputMouseState.BUTTON_4_DOWN) != 0) mouse.EnableBit((int)MouseButton.Button1); + if ((raw.ButtonFlags & RawInputMouseState.BUTTON_4_UP) != 0) mouse.DisableBit((int)MouseButton.Button1); + if ((raw.ButtonFlags & RawInputMouseState.BUTTON_5_DOWN) != 0) mouse.EnableBit((int)MouseButton.Button2); + if ((raw.ButtonFlags & RawInputMouseState.BUTTON_5_UP) != 0) mouse.DisableBit((int)MouseButton.Button2); + + if ((raw.ButtonFlags & RawInputMouseState.WHEEL) != 0) + mouse.WheelPrecise += (short)raw.ButtonData / 120.0f; + + if ((raw.Flags & RawMouseFlags.MOUSE_MOVE_ABSOLUTE) != 0) + { + mouse.X = raw.LastX; + mouse.Y = raw.LastY; + } + else + { // Seems like MOUSE_MOVE_RELATIVE is the default, unless otherwise noted. + mouse.X += raw.LastX; + mouse.Y += raw.LastY; + } + + lock (UpdateLock) + { + mice[mouse_handle] = mouse; + return true; + } } #endregion - #endregion + #region Private Members - #region internal void RegisterRawDevice(OpenTK.Input.Mouse mouse) + static string GetDeviceName(RawInputDeviceList dev) + { + // get name size + uint size = 0; + Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size); - internal void RegisterRawDevice(OpenTK.Input.MouseDevice mouse) + // get actual name + IntPtr name_ptr = Marshal.AllocHGlobal((IntPtr)size); + Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, name_ptr, ref size); + string name = Marshal.PtrToStringAnsi(name_ptr); + Marshal.FreeHGlobal(name_ptr); + + return name; + } + + static RegistryKey FindRegistryKey(string name) + { + // remove the \??\ + name = name.Substring(4); + + string[] split = name.Split('#'); + + string id_01 = split[0]; // ACPI (Class code) + string id_02 = split[1]; // PNP0303 (SubClass code) + string id_03 = split[2]; // 3&13c0b0c5&0 (Protocol code) + // The final part is the class GUID and is not needed here + + string findme = string.Format( + @"System\CurrentControlSet\Enum\{0}\{1}\{2}", + id_01, id_02, id_03); + + RegistryKey regkey = Registry.LocalMachine.OpenSubKey(findme); + return regkey; + } + + static void RegisterRawDevice(IntPtr window, string device) { RawInputDevice[] rid = new RawInputDevice[1]; // Mouse is 1/2 (page/id). See http://www.microsoft.com/whdc/device/input/HID_HWID.mspx @@ -145,113 +241,46 @@ namespace OpenTK.Platform.Windows if (!Functions.RegisterRawInputDevices(rid, 1, API.RawInputDeviceSize)) { - throw new ApplicationException( - String.Format( - "Raw input registration failed with error: {0}. Device: {1}", - Marshal.GetLastWin32Error(), - rid[0].ToString()) - ); + Debug.Print("[Warning] Raw input registration failed with error: {0}. Device: {1}", + Marshal.GetLastWin32Error(), rid[0].ToString()); } else { - Debug.Print("Registered mouse {0}", mouse.ToString()); - Point p = new Point(); - if (Functions.GetCursorPos(ref p)) - mouse.Position = p; + Debug.Print("Registered mouse {0}", device); } } #endregion - #region internal bool ProcessEvent(API.RawInput rin) + #region IMouseDriver2 Members - /// - /// Processes raw input events. - /// - /// - /// - internal bool ProcessEvent(RawInput rin) + public MouseState GetState() { - //MouseDevice mouse = mice.Find(delegate(MouseDevice m) - //{ - // return m.DeviceID == rin.Header.Device; - //}); - MouseDevice mouse; - if (mice.Count > 0) mouse = mice[0]; - else return false; - - switch (rin.Header.Type) + lock (UpdateLock) { - case RawInputDeviceType.MOUSE: - if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.LEFT_BUTTON_DOWN) != 0) mouse[MouseButton.Left] = true; - if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.LEFT_BUTTON_UP) != 0) mouse[MouseButton.Left] = false; - if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.RIGHT_BUTTON_DOWN) != 0) mouse[MouseButton.Right] = true; - if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.RIGHT_BUTTON_UP) != 0) mouse[MouseButton.Right] = false; - if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.MIDDLE_BUTTON_DOWN) != 0) mouse[MouseButton.Middle] = true; - if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.MIDDLE_BUTTON_UP) != 0) mouse[MouseButton.Middle] = false; - if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.BUTTON_4_DOWN) != 0) mouse[MouseButton.Button1] = true; - if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.BUTTON_4_UP) != 0) mouse[MouseButton.Button1] = false; - if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.BUTTON_5_DOWN) != 0) mouse[MouseButton.Button2] = true; - if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.BUTTON_5_UP) != 0) mouse[MouseButton.Button2] = false; - - if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.WHEEL) != 0) - mouse.Wheel += (short)rin.Data.Mouse.ButtonData / 120; - - if ((rin.Data.Mouse.Flags & RawMouseFlags.MOUSE_MOVE_ABSOLUTE) != 0) - { - mouse.Position = new Point(rin.Data.Mouse.LastX, rin.Data.Mouse.LastY); - } - else - { // Seems like MOUSE_MOVE_RELATIVE is the default, unless otherwise noted. - mouse.Position = new Point(mouse.X + rin.Data.Mouse.LastX, - mouse.Y + rin.Data.Mouse.LastY); - } - - if ((rin.Data.Mouse.Flags & RawMouseFlags.MOUSE_VIRTUAL_DESKTOP) != 0) - Debug.WriteLine(String.Format("Mouse {0} defines MOUSE_VIRTUAL_DESKTOP flag, please report at http://www.opentk.com", mouse.ToString())); - - return true; - - default: - throw new ApplicationException("WinRawMouse driver received invalid data."); - } - } - - #endregion - - #region public void Poll() - - public void Poll() - { - } - - #endregion - - #region --- IDisposable Members --- - - private bool disposed; - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool manual) - { - if (!disposed) - { - if (manual) + MouseState master = new MouseState(); + foreach (MouseState ms in mice) { - mice.Clear(); + master.MergeBits(ms); } - disposed = true; + return master; } } - ~WinRawMouse() + public MouseState GetState(int index) { - Dispose(false); + lock (UpdateLock) + { + if (mice.Count > index) + return mice[index]; + else + return new MouseState(); + } + } + + public void SetPosition(double x, double y) + { + Functions.SetCursorPos((int)x, (int)y); } #endregion diff --git a/Source/OpenTK/Platform/X11/API.cs b/Source/OpenTK/Platform/X11/API.cs index 442842e5..dd7a5d25 100644 --- a/Source/OpenTK/Platform/X11/API.cs +++ b/Source/OpenTK/Platform/X11/API.cs @@ -1142,6 +1142,8 @@ XF86VidModeGetGammaRampSize( Hyper_L = 0xffed, /* Left hyper */ Hyper_R = 0xffee, /* Right hyper */ + ISO_Level3_Shift = 0xfe03, + /* * Latin 1 * (ISO/IEC 8859-1 = Unicode U+0020..U+00FF) @@ -1245,6 +1247,79 @@ XF86VidModeGetGammaRampSize( bar = 0x007c, /* U+007C VERTICAL LINE */ braceright = 0x007d, /* U+007D RIGHT CURLY BRACKET */ asciitilde = 0x007e, /* U+007E TILDE */ + + // Extra keys + + XF86AudioMute = 0x1008ff12, + XF86AudioLowerVolume = 0x1008ff11, + XF86AudioRaiseVolume = 0x1008ff13, + XF86PowerOff = 0x1008ff2a, + XF86Suspend = 0x1008ffa7, + XF86Copy = 0x1008ff57, + XF86Paste = 0x1008ff6d, + XF86Cut = 0x1008ff58, + XF86MenuKB = 0x1008ff65, + XF86Calculator = 0x1008ff1d, + XF86Sleep = 0x1008ff2f, + XF86WakeUp = 0x1008ff2b , + XF86Explorer = 0x1008ff5d, + XF86Send = 0x1008ff7b, + XF86Xfer = 0x1008ff8a, + XF86Launch1 = 0x1008ff41, + XF86Launch2 = 0x1008ff42, + XF86Launch3 = 0x1008ff43, + XF86Launch4 = 0x1008ff44, + XF86Launch5 = 0x1008ff45, + XF86LaunchA = 0x1008ff4a, + XF86LaunchB = 0x1008ff4b, + XF86WWW = 0x1008ff2e, + XF86DOS = 0x1008ff5a, + XF86ScreenSaver = 0x1008ff2d, + XF86RotateWindows = 0x1008ff74, + XF86Mail = 0x1008ff19, + XF86Favorites = 0x1008ff30, + XF86MyComputer = 0x1008ff33, + XF86Back = 0x1008ff26, + XF86Forward = 0x1008ff27 , + XF86Eject = 0x1008ff2c, + XF86AudioPlay = 0x1008ff14, + XF86AudioStop = 0x1008ff15, + XF86AudioPrev = 0x1008ff16, + XF86AudioNext = 0x1008ff17, + XF86AudioRecord = 0x1008ff1c, + XF86AudioPause =0x1008ff31, + XF86AudioRewind = 0x1008ff3e, + XF86AudioForward = 0x1008ff97, + XF86Phone = 0x1008ff6e, + XF86Tools = 0x1008ff81, + XF86HomePage = 0x1008ff18, + XF86Close = 0x1008ff56, + XF86Reload = 0x1008ff73, + XF86ScrollUp = 0x1008ff78, + XF86ScrollDown = 0x1008ff79, + XF86New = 0x1008ff68, + XF86TouchpadToggle = 0x1008ffa9, + XF86WebCam = 0x1008ff8f, + XF86Search = 0x1008ff1b, + XF86Finance = 0x1008ff3c, + XF86Shop = 0x1008ff36, + XF86MonBrightnessDown = 0x1008ff03, + XF86MonBrightnessUp = 0x1008ff02, + XF86AudioMedia = 0x1008ff32, + XF86Display = 0x1008ff59, + XF86KbdLightOnOff = 0x1008ff04, + XF86KbdBrightnessDown = 0x1008ff06, + XF86KbdBrightnessUp = 0x1008ff05, + XF86Reply = 0x1008ff72, + XF86MailForward = 0x1008ff90, + XF86Save = 0x1008ff77, + XF86Documents = 0x1008ff5b, + XF86Battery = 0x1008ff93, + XF86Bluetooth = 0x1008ff94, + XF86WLAN = 0x1008ff95, + + SunProps = 0x1005ff70, + SunOpen = 0x1005ff73, } #endregion @@ -1426,7 +1501,7 @@ XF86VidModeGetGammaRampSize( [DllImport(XrandrLibrary)] public static extern Status XRRSetScreenConfig(Display dpy, XRRScreenConfiguration config, - Drawable draw, int size_index, ref Rotation rotation, Time timestamp); + Drawable draw, int size_index, Rotation rotation, Time timestamp); [DllImport(XrandrLibrary)] public static extern Status XRRSetScreenConfigAndRate(Display dpy, XRRScreenConfiguration config, @@ -1562,6 +1637,26 @@ XF86VidModeGetGammaRampSize( #endregion #endregion + + public static Pixmap XCreateBitmapFromData(Display display, Window d, byte[,] data) + { + if (data == null) + throw new ArgumentNullException("data"); + + unsafe + { + fixed (byte* pdata = data) + { + return XCreateBitmapFromData(display, d, pdata, data.GetLength(0), data.GetLength(1)); + } + } + } + + [DllImport(X11Library)] + unsafe public static extern Pixmap XCreateBitmapFromData(Display display, Window d, byte* data, int width, int height); + + [DllImport("libX11", EntryPoint = "XAllocColor")] + public static extern Status XAllocNamedColor(Display display, Colormap colormap, string color_name, out XColor screen_def_return, out XColor exact_def_return); } /* [StructLayout(LayoutKind.Sequential)] @@ -1618,6 +1713,19 @@ XF86VidModeGetGammaRampSize( Functions.XUnlockDisplay(Display); } } + + // XAllowEvent modes + enum EventMode + { + AsyncPointer = 0, + SyncPointer, + ReplayPointer, + AsyncKeyboard, + SyncKeyboard, + ReplayKeyboard, + AsyncBoth, + SyncBoth + } } #pragma warning restore 3019 diff --git a/Source/OpenTK/Platform/X11/Functions.cs b/Source/OpenTK/Platform/X11/Functions.cs index 1cbf5f97..7d138d92 100644 --- a/Source/OpenTK/Platform/X11/Functions.cs +++ b/Source/OpenTK/Platform/X11/Functions.cs @@ -27,7 +27,7 @@ namespace OpenTK.Platform.X11 using Mask = System.IntPtr; using Atom = System.IntPtr; using VisualID = System.IntPtr; - using Time = System.UInt32; + using Time = System.IntPtr; using KeyCode = System.Byte; // Or maybe ushort? using Display = System.IntPtr; @@ -94,13 +94,23 @@ namespace OpenTK.Platform.X11 public extern static Bool XCheckWindowEvent(Display display, Window w, EventMask event_mask, ref XEvent event_return); [DllImport("libX11")] public extern static Bool XCheckTypedWindowEvent(Display display, Window w, XEventName event_type, ref XEvent event_return); - + [DllImport("libX11")] + public extern static Bool XCheckTypedEvent(Display display, XEventName event_type, out XEvent event_return); + + + public delegate Bool EventPredicate(IntPtr display, ref XEvent e, IntPtr arg); + [DllImport("libX11")] + public extern static Bool XIfEvent(Display display, ref XEvent e, IntPtr predicate, IntPtr arg ); + [DllImport("libX11")] + public extern static Bool XCheckIfEvent(Display display, ref XEvent e, IntPtr predicate, IntPtr arg ); + [DllImport("libX11")] public extern static int XConnectionNumber(IntPtr diplay); [DllImport("libX11")] public extern static int XPending(IntPtr diplay); + [DllImport("libX11", EntryPoint = "XSelectInput")] - public extern static IntPtr XSelectInput(IntPtr display, IntPtr window, IntPtr mask); + public extern static int XSelectInput(IntPtr display, IntPtr window, IntPtr mask); [DllImport("libX11", EntryPoint = "XDestroyWindow")] public extern static int XDestroyWindow(IntPtr display, IntPtr window); @@ -170,12 +180,41 @@ namespace OpenTK.Platform.X11 [DllImport("libX11", EntryPoint = "XUngrabPointer")] public extern static int XUngrabPointer(IntPtr display, IntPtr timestamp); + [DllImport("libX11", EntryPoint = "XGrabButton")] + public extern static int XGrabButton(IntPtr display, + int button, uint modifiers, Window grab_window, + Bool owner_events, EventMask event_mask, + GrabMode pointer_mode, GrabMode keyboard_mode, + Window confine_to, Cursor cursor); + + [DllImport("libX11", EntryPoint = "XUngrabButton")] + public extern static int XUngrabButton(IntPtr display, uint button, uint + modifiers, Window grab_window); + [DllImport("libX11", EntryPoint = "XQueryPointer")] public extern static bool XQueryPointer(IntPtr display, IntPtr window, out IntPtr root, out IntPtr child, out int root_x, out int root_y, out int win_x, out int win_y, out int keys_buttons); [DllImport("libX11", EntryPoint = "XTranslateCoordinates")] public extern static bool XTranslateCoordinates(IntPtr display, IntPtr src_w, IntPtr dest_w, int src_x, int src_y, out int intdest_x_return, out int dest_y_return, out IntPtr child_return); + + [DllImport("libX11")] + public extern static int XGrabKey(IntPtr display, int keycode, uint modifiers, + Window grab_window, bool owner_events, GrabMode pointer_mode, GrabMode keyboard_mode); + + [DllImport("libX11")] + public extern static int XUngrabKey(IntPtr display, int keycode, uint modifiers, Window grab_window); + + [DllImport("libX11", EntryPoint = "XGrabKeyboard")] + public extern static int XGrabKeyboard(IntPtr display, IntPtr window, bool owner_events, + GrabMode pointer_mode, GrabMode keyboard_mode, IntPtr timestamp); + + [DllImport("libX11", EntryPoint = "XUngrabKeyboard")] + public extern static int XUngrabKeyboard(IntPtr display, IntPtr timestamp); + + [DllImport("libX11")] + public extern static int XAllowEvents(IntPtr display, EventMode event_mode, Time time); + [DllImport("libX11", EntryPoint = "XGetGeometry")] public extern static bool XGetGeometry(IntPtr display, IntPtr window, out IntPtr root, out int x, out int y, out int width, out int height, out int border_width, out int depth); @@ -319,7 +358,7 @@ namespace OpenTK.Platform.X11 public extern static int XQueryBestCursor(IntPtr display, IntPtr drawable, int width, int height, out int best_width, out int best_height); [DllImport("libX11", EntryPoint = "XQueryExtension")] - public extern static int XQueryExtension(IntPtr display, string extension_name, ref int major, ref int first_event, ref int first_error); + public extern static int XQueryExtension(IntPtr display, string extension_name, out int major, out int first_event, out int first_error); [DllImport("libX11", EntryPoint = "XWhitePixel")] public extern static IntPtr XWhitePixel(IntPtr display, int screen_no); @@ -453,9 +492,49 @@ namespace OpenTK.Platform.X11 public static extern int XLookupString(ref XKeyEvent event_struct, [Out] byte[] buffer_return, int bytes_buffer, [Out] KeySym[] keysym_return, IntPtr status_in_out); + [DllImport("libX11")] + public static extern KeyCode XKeysymToKeycode(IntPtr display, KeySym keysym); + + [DllImport("libX11")] + public static extern KeySym XKeycodeToKeysym(IntPtr display, KeyCode keycode, int index); + [DllImport("libX11")] public static extern int XRefreshKeyboardMapping(ref XMappingEvent event_map); + [DllImport("libX11")] + public static extern int XGetEventData(IntPtr display, ref XGenericEventCookie cookie); + + [DllImport("libX11")] + public static extern void XFreeEventData(IntPtr display, ref XGenericEventCookie cookie); + + [DllImport("libXi")] + static extern int XISelectEvents(IntPtr dpy, Window win, [In] XIEventMask[] masks, int num_masks); + [DllImport("libXi")] + static extern int XISelectEvents(IntPtr dpy, Window win, [In] ref XIEventMask masks, int num_masks); + + public static int XISelectEvents(IntPtr dpy, Window win, XIEventMask[] masks) + { + return XISelectEvents(dpy, win, masks, masks.Length); + } + + public static int XISelectEvents(IntPtr dpy, Window win, XIEventMask mask) + { + return XISelectEvents(dpy, win, ref mask, 1); + } + + [DllImport("libXi")] + static extern Status XIGrabDevice(IntPtr display, int deviceid, Window grab_window, Time time, + Cursor cursor, int grab_mode, int paired_device_mode, Bool owner_events, XIEventMask[] mask); + + [DllImport("libXi")] + static extern Status XIUngrabDevice(IntPtr display, int deviceid, Time time); + + [DllImport("libXi")] + public static extern Bool XIWarpPointer(Display display, + int deviceid, Window src_w, Window dest_w, + double src_x, double src_y, int src_width, int src_height, + double dest_x, double dest_y); + static readonly IntPtr CopyFromParent = IntPtr.Zero; public static void SendNetWMMessage(X11WindowInfo window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2) diff --git a/Source/OpenTK/Platform/X11/Structs.cs b/Source/OpenTK/Platform/X11/Structs.cs index bc7e84a7..ac20cfe4 100644 --- a/Source/OpenTK/Platform/X11/Structs.cs +++ b/Source/OpenTK/Platform/X11/Structs.cs @@ -33,9 +33,16 @@ using System.Drawing; using System.Diagnostics; using System.Runtime.InteropServices; +// Disable unused field warnings. This is interop, we don't use everything +#pragma warning disable 0649 + // X11 Version namespace OpenTK.Platform.X11 { + using Bool = System.Boolean; + using Time = System.IntPtr; + using Window = System.IntPtr; + // // In the structures below, fields of type long are mapped to IntPtr. // This will work on all platforms where sizeof(long)==sizeof(void*), which @@ -545,6 +552,29 @@ namespace OpenTK.Platform.X11 public IntPtr pad23; } + [StructLayout(LayoutKind.Sequential)] + internal struct XGenericEvent + { + public int type; // of event. Always GenericEvent + public IntPtr serial; // # of last request processed + public bool send_event; // true if from SendEvent request + public IntPtr display; // Display the event was read from + public int extension; // major opcode of extension that caused the event + public int evtype; // actual event type. + } + + internal struct XGenericEventCookie + { + public int type; // of event. Always GenericEvent + public IntPtr serial; // # of last request processed + public bool send_event; // true if from SendEvent request + public IntPtr display; // Display the event was read from + public int extension; // major opcode of extension that caused the event + public int evtype; // actual event type. + public uint cookie; // unique event cookie + public IntPtr data; // actual event data + } + [StructLayout(LayoutKind.Explicit)] internal struct XEvent { @@ -612,6 +642,10 @@ namespace OpenTK.Platform.X11 public XErrorEvent ErrorEvent; [FieldOffset(0)] public XKeymapEvent KeymapEvent; + [FieldOffset(0)] + public XGenericEvent GenericEvent; + [FieldOffset(0)] + public XGenericEventCookie GenericEventCookie; //[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=24)] //[ FieldOffset(0) ] public int[] pad; @@ -753,6 +787,7 @@ namespace OpenTK.Platform.X11 ColormapNotify = 32, ClientMessage = 33, MappingNotify = 34, + GenericEvent = 35, LASTEvent } @@ -1623,4 +1658,164 @@ namespace OpenTK.Platform.X11 XYPixmap = 1, ZPixmap } + + // XInput2 structures + + struct XIDeviceInfo + { + public int deviceid; + public IntPtr name; // byte* + public int use; + public int attachment; + public Bool enabled; + public int num_classes; + public IntPtr classes; // XIAnyClassInfo ** + } + + struct XIAnyClassInfo + { + public int type; + public int sourceid; + } + + struct XIDeviceEvent + { + public int type; /* GenericEvent */ + public IntPtr serial; /* # of last request processed by server */ + public bool send_event; /* true if this came from a SendEvent request */ + public IntPtr display; /* Display the event was read from */ + public int extension; /* XI extension offset */ + public XIEventType evtype; + public Time time; + public int deviceid; + public int sourceid; + public int detail; + public Window root; + public Window @event; + public Window child; + public double root_x; + public double root_y; + public double event_x; + public double event_y; + public int flags; + public XIButtonState buttons; + public XIValuatorState valuators; + public XIModifierState mods; + public XIGroupState @group; + } + + struct XIRawEvent + { + public int type; /* GenericEvent */ + public IntPtr serial; /* # of last request processed by server */ + public Bool send_event; /* true if this came from a SendEvent request */ + public IntPtr display; /* Display the event was read from */ + public int extension; /* XI extension offset */ + public XIEventType evtype; /* XI_RawKeyPress, XI_RawKeyRelease, etc. */ + public Time time; + public int deviceid; + public int sourceid; + public int detail; + public int flags; + public XIValuatorState valuators; + public IntPtr raw_values; // double * + } + + struct XIButtonState + { + public int mask_len; + public IntPtr mask; // byte* + } + + struct XIModifierState + { + public int @base; + public int latched; + public int locked; + public int effective; + } + + struct XIGroupState + { + public int @base; + public int latched; + public int locked; + public int effective; + } + + struct XIValuatorState + { + public int mask_len; + public IntPtr mask; // byte* + public IntPtr values; // double* + } + + struct XIEventMask : IDisposable + { + public int deviceid; // 0 = XIAllDevices, 1 = XIAllMasterDevices + int mask_len; + unsafe XIEventMasks* mask; + + public XIEventMask(int id, XIEventMasks m) + { + deviceid = id; + mask_len = sizeof(XIEventMasks); + unsafe + { + mask = (XIEventMasks*)Marshal.AllocHGlobal(mask_len); + *mask = m; + } + } + + public void Dispose() + { + unsafe + { + Marshal.FreeHGlobal(new IntPtr((void*)mask)); + } + } + } + + enum XIEventType + { + DeviceChanged = 1, + KeyPress, + KeyRelease, + ButtonPress, + ButtonRelease, + Motion, + Enter, + Leave, + FocusIn, + FocusOut, + HierarchyChanged, + PropertyEvent, + RawKeyPress, + RawKeyRelease, + RawButtonPress, + RawButtonRelease, + RawMotion, + LastEvent = RawMotion + } + + enum XIEventMasks + { + DeviceChangedMask = (1 << (int)XIEventType.DeviceChanged), + KeyPressMask = (1 << (int)XIEventType.KeyPress), + KeyReleaseMask = (1 << (int)XIEventType.KeyRelease), + ButtonPressMask = (1 << (int)XIEventType.ButtonPress), + ButtonReleaseMask = (1 << (int)XIEventType.ButtonRelease), + MotionMask = (1 << (int)XIEventType.Motion), + EnterMask = (1 << (int)XIEventType.Enter), + LeaveMask = (1 << (int)XIEventType.Leave), + FocusInMask = (1 << (int)XIEventType.FocusIn), + FocusOutMask = (1 << (int)XIEventType.FocusOut), + HierarchyChangedMask = (1 << (int)XIEventType.HierarchyChanged), + PropertyEventMask = (1 << (int)XIEventType.PropertyEvent), + RawKeyPressMask = (1 << (int)XIEventType.RawKeyPress), + RawKeyReleaseMask = (1 << (int)XIEventType.RawKeyRelease), + RawButtonPressMask = (1 << (int)XIEventType.RawButtonPress), + RawButtonReleaseMask = (1 << (int)XIEventType.RawButtonRelease), + RawMotionMask = (1 << (int)XIEventType.RawMotion), + } } diff --git a/Source/OpenTK/Platform/X11/X11DisplayDevice.cs b/Source/OpenTK/Platform/X11/X11DisplayDevice.cs index 5c2d90b0..3262ce1b 100644 --- a/Source/OpenTK/Platform/X11/X11DisplayDevice.cs +++ b/Source/OpenTK/Platform/X11/X11DisplayDevice.cs @@ -1,9 +1,28 @@ -#region --- License --- -/* Licensed under the MIT/X11 license. - * Copyright (c) 2006-2008 the OpenTK Team. - * This notice may not be removed from any source distribution. - * See license.txt for licensing detailed licensing details. - */ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2010 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 using System; @@ -14,32 +33,40 @@ using System.Runtime.InteropServices; namespace OpenTK.Platform.X11 { - internal class X11DisplayDevice : IDisplayDeviceDriver + sealed class X11DisplayDevice : DisplayDeviceBase { - static object display_lock = new object(); // Store a mapping between resolutions and their respective // size_index (needed for XRRSetScreenConfig). The size_index // is simply the sequence number of the resolution as returned by // XRRSizes. This is done per available screen. - static List> screenResolutionToIndex = + readonly List> screenResolutionToIndex = new List>(); // Store a mapping between DisplayDevices and their default resolutions. - static Dictionary deviceToDefaultResolution = new Dictionary(); + readonly Dictionary deviceToDefaultResolution = new Dictionary(); // Store a mapping between DisplayDevices and X11 screens. - static Dictionary deviceToScreen = new Dictionary(); + readonly Dictionary deviceToScreen = new Dictionary(); // Keep the time when the config of each screen was last updated. - static List lastConfigUpdate = new List(); + readonly List lastConfigUpdate = new List(); - static bool xinerama_supported, xrandr_supported, xf86_supported; + bool xinerama_supported, xrandr_supported, xf86_supported; - #region --- Constructors --- + #region Constructors - static X11DisplayDevice() + public X11DisplayDevice() + { + RefreshDisplayDevices(); + } + + #endregion + + #region Private Methods + + void RefreshDisplayDevices() { using (new XLock(API.DefaultDisplay)) { List devices = new List(); - bool xinerama_supported = false; + xinerama_supported = false; try { xinerama_supported = QueryXinerama(devices); @@ -82,16 +109,23 @@ namespace OpenTK.Platform.X11 Debug.Print("XF86 query failed, no DisplayDevice support available."); } } + + AvailableDevices.Clear(); + AvailableDevices.AddRange(devices); + Primary = FindDefaultDevice(devices); } } - internal X11DisplayDevice() { } + static DisplayDevice FindDefaultDevice(IEnumerable devices) + { + foreach (DisplayDevice dev in devices) + if (dev.IsPrimary) + return dev; - #endregion + throw new InvalidOperationException("No primary display found. Please file a bug at http://www.opentk.com"); + } - #region --- Private Methods --- - - static bool QueryXinerama(List devices) + bool QueryXinerama(List devices) { // Try to use Xinerama to obtain the geometry of all output devices. int event_base, error_base; @@ -119,7 +153,7 @@ namespace OpenTK.Platform.X11 return (devices.Count>0); } - static bool QueryXRandR(List devices) + bool QueryXRandR(List devices) { // Get available resolutions. Then, for each resolution get all available rates. foreach (DisplayDevice dev in devices) @@ -196,7 +230,7 @@ namespace OpenTK.Platform.X11 return true; } - static bool QueryXF86(List devices) + bool QueryXF86(List devices) { int major; int minor; @@ -286,14 +320,14 @@ namespace OpenTK.Platform.X11 #region private static int FindCurrentDepth(int screen) - private static int FindCurrentDepth(int screen) + static int FindCurrentDepth(int screen) { return (int)Functions.XDefaultDepth(API.DefaultDisplay, screen); } #endregion - static bool ChangeResolutionXRandR(DisplayDevice device, DisplayResolution resolution) + bool ChangeResolutionXRandR(DisplayDevice device, DisplayResolution resolution) { using (new XLock(API.DefaultDisplay)) { @@ -313,8 +347,28 @@ namespace OpenTK.Platform.X11 Debug.Print("Changing size of screen {0} from {1} to {2}", screen, current_resolution_index, new_resolution_index); - return 0 == Functions.XRRSetScreenConfigAndRate(API.DefaultDisplay, screen_config, root, new_resolution_index, - current_rotation, (short)(resolution != null ? resolution.RefreshRate : 0), lastConfigUpdate[screen]); + int ret = 0; + short refresh_rate =(short)(resolution != null ? resolution.RefreshRate : 0); + if (refresh_rate > 0) + { + ret = Functions.XRRSetScreenConfigAndRate(API.DefaultDisplay, + screen_config, root, new_resolution_index, current_rotation, + refresh_rate, IntPtr.Zero); + } + else + { + ret = Functions.XRRSetScreenConfig(API.DefaultDisplay, + screen_config, root, new_resolution_index, current_rotation, + IntPtr.Zero); + } + + if (ret != 0) + { + Debug.Print("[Error] Change to resolution {0} failed with error {1}.", + resolution, (ErrorCode)ret); + } + + return ret == 0; } } @@ -325,9 +379,9 @@ namespace OpenTK.Platform.X11 #endregion - #region --- IDisplayDeviceDriver Members --- + #region IDisplayDeviceDriver Members - public bool TryChangeResolution(DisplayDevice device, DisplayResolution resolution) + public sealed override bool TryChangeResolution(DisplayDevice device, DisplayResolution resolution) { // If resolution is null, restore the default resolution (new_resolution_index = 0). @@ -345,8 +399,7 @@ namespace OpenTK.Platform.X11 } } - - public bool TryRestoreResolution(DisplayDevice device) + public sealed override bool TryRestoreResolution(DisplayDevice device) { return TryChangeResolution(device, null); } diff --git a/Source/OpenTK/Platform/X11/X11Factory.cs b/Source/OpenTK/Platform/X11/X11Factory.cs index 97ddf3e2..6fd9c5eb 100644 --- a/Source/OpenTK/Platform/X11/X11Factory.cs +++ b/Source/OpenTK/Platform/X11/X11Factory.cs @@ -78,9 +78,17 @@ namespace OpenTK.Platform.X11 return new X11GraphicsMode(); } - public virtual OpenTK.Input.IKeyboardDriver CreateKeyboardDriver() + public virtual OpenTK.Input.IKeyboardDriver2 CreateKeyboardDriver() { - throw new NotImplementedException(); + return new X11Keyboard(); + } + + public virtual OpenTK.Input.IMouseDriver2 CreateMouseDriver() + { + if (XI2Mouse.IsSupported(IntPtr.Zero)) + return new XI2Mouse(); // Requires xorg 1.7 or higher. + else + return new X11Mouse(); // Always supported. } #endregion diff --git a/Source/OpenTK/Platform/X11/X11GLNative.cs b/Source/OpenTK/Platform/X11/X11GLNative.cs index 7119095f..4be53acd 100644 --- a/Source/OpenTK/Platform/X11/X11GLNative.cs +++ b/Source/OpenTK/Platform/X11/X11GLNative.cs @@ -54,7 +54,10 @@ namespace OpenTK.Platform.X11 const int _min_width = 30, _min_height = 30; X11WindowInfo window = new X11WindowInfo(); + + // Legacy input support X11Input driver; + MouseDevice mouse; // Window manager hints for fullscreen windows. // Not used right now (the code is written, but is not 64bit-correct), but could be useful for older WMs which @@ -107,12 +110,18 @@ namespace OpenTK.Platform.X11 bool isExiting; bool _decorations_hidden = false; + bool cursor_visible = true; + int mouse_rel_x, mouse_rel_y; // Keyboard input readonly byte[] ascii = new byte[16]; readonly char[] chars = new char[16]; readonly KeyPressEventArgs KPEventArgs = new KeyPressEventArgs('\0'); + readonly IntPtr EmptyCursor; + + public static bool MouseWarpActive = false; + #endregion #region Constructors @@ -194,6 +203,9 @@ namespace OpenTK.Platform.X11 RefreshWindowBounds(ref e); driver = new X11Input(window); + mouse = driver.Mouse[0]; + + EmptyCursor = CreateEmptyCursor(window); Debug.WriteLine(String.Format("X11GLNative window created successfully (id: {0}).", Handle)); Debug.Unindent(); @@ -655,8 +667,7 @@ namespace OpenTK.Platform.X11 if (Location != new_location) { bounds.Location = new_location; - if (Move != null) - Move(this, EventArgs.Empty); + Move(this, EventArgs.Empty); } // Note: width and height denote the internal (client) size. @@ -669,14 +680,37 @@ namespace OpenTK.Platform.X11 bounds.Size = new_size; client_rectangle.Size = new Size(e.ConfigureEvent.width, e.ConfigureEvent.height); - if (this.Resize != null) - { - //Debug.WriteLine(new System.Diagnostics.StackTrace()); - Resize(this, EventArgs.Empty); - } + Resize(this, EventArgs.Empty); } } + static IntPtr CreateEmptyCursor(X11WindowInfo window) + { + IntPtr cursor = IntPtr.Zero; + using (new XLock(window.Display)) + { + XColor black, dummy; + IntPtr cmap = Functions.XDefaultColormap(window.Display, window.Screen); + Functions.XAllocNamedColor(window.Display, cmap, "black", out black, out dummy); + IntPtr bmp_empty = Functions.XCreateBitmapFromData(window.Display, + window.WindowHandle, new byte[,] { { 0 } }); + cursor = Functions.XCreatePixmapCursor(window.Display, + bmp_empty, bmp_empty, ref black, ref black, 0, 0); + } + return cursor; + } + + static void SetMouseClamped(MouseDevice mouse, int x, int y, + int left, int top, int width, int height) + { + // Clamp mouse to the specified rectangle. + x = Math.Max(x, left); + x = Math.Min(x, width); + y = Math.Max(y, top); + y = Math.Min(y, height); + mouse.Position = new Point(x, y); + } + #endregion #region INativeWindow Members @@ -703,8 +737,7 @@ namespace OpenTK.Platform.X11 bool previous_visible = visible; visible = true; if (visible != previous_visible) - if (VisibleChanged != null) - VisibleChanged(this, EventArgs.Empty); + VisibleChanged(this, EventArgs.Empty); } return; @@ -713,8 +746,7 @@ namespace OpenTK.Platform.X11 bool previous_visible = visible; visible = false; if (visible != previous_visible) - if (VisibleChanged != null) - VisibleChanged(this, EventArgs.Empty); + VisibleChanged(this, EventArgs.Empty); } break; @@ -727,8 +759,7 @@ namespace OpenTK.Platform.X11 { Debug.WriteLine("Exit message received."); CancelEventArgs ce = new CancelEventArgs(); - if (Closing != null) - Closing(this, ce); + Closing(this, ce); if (!ce.Cancel) { @@ -749,8 +780,7 @@ namespace OpenTK.Platform.X11 Debug.WriteLine("Window destroyed"); exists = false; - if (Closed != null) - Closed(this, EventArgs.Empty); + Closed(this, EventArgs.Empty); return; @@ -783,6 +813,45 @@ namespace OpenTK.Platform.X11 break; case XEventName.MotionNotify: + { + // Try to detect and ignore events from XWarpPointer, below. + // This heuristic will fail if the user actually moves the pointer + // to the dead center of the window. Fortunately, this situation + // is very very uncommon. Todo: Can this be remedied? + int x = e.MotionEvent.x; + int y =e.MotionEvent.y; + int middle_x = (Bounds.Left + Bounds.Right) / 2; + int middle_y = (Bounds.Top + Bounds.Bottom) / 2; + Point screen_xy = PointToScreen(new Point(x, y)); + if (!CursorVisible && MouseWarpActive && + screen_xy.X == middle_x && screen_xy.Y == middle_y) + { + MouseWarpActive = false; + mouse_rel_x = x; + mouse_rel_y = y; + } + else if (!CursorVisible) + { + SetMouseClamped(mouse, + mouse.X + x - mouse_rel_x, + mouse.Y + y - mouse_rel_y, + 0, 0, Width, Height); + mouse_rel_x = x; + mouse_rel_y = y; + + // Warp cursor to center of window. + MouseWarpActive = true; + Mouse.SetPosition(middle_x, middle_y); + } + else + { + SetMouseClamped(mouse, x, y, 0, 0, Width, Height); + mouse_rel_x = x; + mouse_rel_y = y; + } + break; + } + case XEventName.ButtonPress: case XEventName.ButtonRelease: driver.ProcessEvent(ref e); @@ -793,8 +862,7 @@ namespace OpenTK.Platform.X11 bool previous_focus = has_focus; has_focus = true; if (has_focus != previous_focus) - if (FocusedChanged != null) - FocusedChanged(this, EventArgs.Empty); + FocusedChanged(this, EventArgs.Empty); } break; @@ -803,19 +871,19 @@ namespace OpenTK.Platform.X11 bool previous_focus = has_focus; has_focus = false; if (has_focus != previous_focus) - if (FocusedChanged != null) - FocusedChanged(this, EventArgs.Empty); + FocusedChanged(this, EventArgs.Empty); } break; case XEventName.LeaveNotify: - if (MouseLeave != null) + if (CursorVisible) + { MouseLeave(this, EventArgs.Empty); + } break; case XEventName.EnterNotify: - if (MouseEnter != null) - MouseEnter(this, EventArgs.Empty); + MouseEnter(this, EventArgs.Empty); break; case XEventName.MappingNotify: @@ -830,8 +898,7 @@ namespace OpenTK.Platform.X11 case XEventName.PropertyNotify: if (e.PropertyEvent.atom == _atom_net_wm_state) { - if (WindowStateChanged != null) - WindowStateChanged(this, EventArgs.Empty); + WindowStateChanged(this, EventArgs.Empty); } //if (e.PropertyEvent.atom == _atom_net_frame_extents) @@ -1058,8 +1125,7 @@ namespace OpenTK.Platform.X11 } icon = value; - if (IconChanged != null) - IconChanged(this, EventArgs.Empty); + IconChanged(this, EventArgs.Empty); } } @@ -1257,8 +1323,7 @@ namespace OpenTK.Platform.X11 break; } - if (WindowBorderChanged != null) - WindowBorderChanged(this, EventArgs.Empty); + WindowBorderChanged(this, EventArgs.Empty); } } @@ -1266,40 +1331,49 @@ namespace OpenTK.Platform.X11 #region Events - public event EventHandler Load; - - public event EventHandler Unload; - - public event EventHandler Move; - - public event EventHandler Resize; - - public event EventHandler Closing; - - public event EventHandler Closed; - - public event EventHandler Disposed; - - public event EventHandler IconChanged; - - public event EventHandler TitleChanged; - - public event EventHandler VisibleChanged; - - public event EventHandler FocusedChanged; - - public event EventHandler WindowBorderChanged; - - public event EventHandler WindowStateChanged; - - public event EventHandler KeyPress; - - public event EventHandler MouseEnter; - - public event EventHandler MouseLeave; + public event EventHandler Move = delegate { }; + public event EventHandler Resize = delegate { }; + public event EventHandler Closing = delegate { }; + public event EventHandler Closed = delegate { }; + public event EventHandler Disposed = delegate { }; + public event EventHandler IconChanged = delegate { }; + public event EventHandler TitleChanged = delegate { }; + public event EventHandler VisibleChanged = delegate { }; + public event EventHandler FocusedChanged = delegate { }; + public event EventHandler WindowBorderChanged = delegate { }; + public event EventHandler WindowStateChanged = delegate { }; + public event EventHandler KeyDown = delegate { }; + public event EventHandler KeyPress = delegate { }; + public event EventHandler KeyUp = delegate { }; + public event EventHandler MouseEnter = delegate { }; + public event EventHandler MouseLeave = delegate { }; #endregion + public bool CursorVisible + { + get { return cursor_visible; } + set + { + if (value) + { + using (new XLock(window.Display)) + { + Functions.XUndefineCursor(window.Display, window.WindowHandle); + cursor_visible = true; + } + } + else + { + using (new XLock(window.Display)) + { + Functions.XDefineCursor(window.Display, window.WindowHandle, EmptyCursor); + cursor_visible = false; + } + } + } + } + #endregion #region --- INativeGLWindow Members --- @@ -1379,8 +1453,7 @@ namespace OpenTK.Platform.X11 } } - if (TitleChanged != null) - TitleChanged(this, EventArgs.Empty); + TitleChanged(this, EventArgs.Empty); } } @@ -1521,6 +1594,7 @@ namespace OpenTK.Platform.X11 { using (new XLock(window.Display)) { + Functions.XFreeCursor(window.Display, EmptyCursor); Functions.XDestroyWindow(window.Display, window.WindowHandle); } diff --git a/Source/OpenTK/Platform/X11/X11KeyMap.cs b/Source/OpenTK/Platform/X11/X11KeyMap.cs index 9fba864c..0e2c7e80 100644 --- a/Source/OpenTK/Platform/X11/X11KeyMap.cs +++ b/Source/OpenTK/Platform/X11/X11KeyMap.cs @@ -36,6 +36,7 @@ namespace OpenTK.Platform.X11 this.Add(XKey.Super_R, Key.WinRight); this.Add(XKey.Meta_L, Key.WinLeft); this.Add(XKey.Meta_R, Key.WinRight); + this.Add(XKey.ISO_Level3_Shift, Key.AltRight); // Normally AltGr this.Add(XKey.Menu, Key.Menu); this.Add(XKey.Tab, Key.Tab); diff --git a/Source/OpenTK/Platform/X11/X11Keyboard.cs b/Source/OpenTK/Platform/X11/X11Keyboard.cs new file mode 100644 index 00000000..ef1f0a0b --- /dev/null +++ b/Source/OpenTK/Platform/X11/X11Keyboard.cs @@ -0,0 +1,125 @@ + #region License + // + // The Open Toolkit Library License + // + // Copyright (c) 2006 - 2010 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 + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using OpenTK.Input; + +namespace OpenTK.Platform.X11 +{ + // Standard keyboard driver that relies on xlib input events. + // Only one keyboard supported. + sealed class X11Keyboard : IKeyboardDriver2 + { + readonly static X11KeyMap keymap = new X11KeyMap(); + readonly static string name = "Core X11 keyboard"; + readonly byte[] keys = new byte[32]; + readonly int KeysymsPerKeycode; + KeyboardState state = new KeyboardState(); + + public X11Keyboard() + { + Debug.WriteLine("Using X11Keyboard."); + state.IsConnected = true; + + IntPtr display = API.DefaultDisplay; + using (new XLock(display)) + { + // Find the number of keysyms per keycode. + int first = 0, last = 0; + API.DisplayKeycodes(display, ref first, ref last); + IntPtr keysym_ptr = API.GetKeyboardMapping(display, (byte)first, last - first + 1, + ref KeysymsPerKeycode); + Functions.XFree(keysym_ptr); + + try + { + // Request that auto-repeat is only set on devices that support it physically. + // This typically means that it's turned off for keyboards what we want). + // We prefer this method over XAutoRepeatOff/On, because the latter needs to + // be reset before the program exits. + bool supported; + Functions.XkbSetDetectableAutoRepeat(display, true, out supported); + } + catch { } + } + } + + public KeyboardState GetState() + { + ProcessEvents(); + return state; + } + + public KeyboardState GetState(int index) + { + // X11Keyboard supports a single keyboard only + ProcessEvents(); + if (index == 0) + return state; + else + return new KeyboardState(); + } + + public string GetDeviceName(int index) + { + if (index == 0) + return name; + else + return String.Empty; + } + + void ProcessEvents() + { + IntPtr display = API.DefaultDisplay; + using (new XLock(display)) + { + Functions.XQueryKeymap(display, keys); + for (int keycode = 8; keycode < 256; keycode++) + { + bool pressed = (keys[keycode >> 3] >> (keycode & 0x07) & 0x01) != 0; + Key key; + + for (int mod = 0; mod < KeysymsPerKeycode; mod++) + { + IntPtr keysym = Functions.XKeycodeToKeysym(display, (byte)keycode, mod); + if (keysym != IntPtr.Zero && keymap.TryGetValue((XKey)keysym, out key)) + { + if (pressed) + state.EnableBit((int)key); + else + state.DisableBit((int)key); + break; + } + } + } + } + } + } +} + diff --git a/Source/OpenTK/Platform/X11/X11Mouse.cs b/Source/OpenTK/Platform/X11/X11Mouse.cs new file mode 100644 index 00000000..7c96545d --- /dev/null +++ b/Source/OpenTK/Platform/X11/X11Mouse.cs @@ -0,0 +1,152 @@ + #region License + // + // The Open Toolkit Library License + // + // Copyright (c) 2006 - 2010 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 + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using OpenTK.Input; + +namespace OpenTK.Platform.X11 +{ + // Note: we cannot create a background window to retrieve events, + // because X11 doesn't deliver core pointer events to background + // windows (unless we grab, which will break *everything*). + // The only solution is to poll. + // Note 2: this driver only supports absolute positions. Relative motion + // is faked through SetPosition. This is called automatically when + // NativeWindow.CursorVisible = false, otherwise it must be called + // by the user. + // Note 3: this driver cannot drive the mouse wheel reliably. + // See comments in ProcessEvents() for more information. + // (If someone knows of a solution, please tell!) + sealed class X11Mouse : IMouseDriver2 + { + readonly IntPtr display; + readonly IntPtr root_window; + MouseState mouse = new MouseState(); + + // When the mouse warps, "detach" the current location + // from the pointer. + bool mouse_detached; + int mouse_detached_x, mouse_detached_y; + + public X11Mouse() + { + Debug.WriteLine("Using X11Mouse."); + mouse.IsConnected = true; + display = API.DefaultDisplay; + root_window = Functions.XRootWindow(display, Functions.XDefaultScreen(display)); + } + + public MouseState GetState() + { + ProcessEvents(); + return mouse; + } + + public MouseState GetState(int index) + { + ProcessEvents(); + // X11Mouse supports only one device + if (index == 0) + return mouse; + else + return new MouseState(); + } + + public void SetPosition(double x, double y) + { + // Update the current location, otherwise the pointer + // may become locked (for instance, if we call + // SetPosition too often, like X11GLNative does). + ProcessEvents(); + + using (new XLock(display)) + { + // Mark the expected warp-event so it can be ignored. + mouse_detached = true; + mouse_detached_x = (int)x; + mouse_detached_y = (int)y; + + Functions.XWarpPointer(display, + IntPtr.Zero, root_window, 0, 0, 0, 0, (int)x, (int)y); + } + } + + void WriteBit(MouseButton offset, int enabled) + { + if (enabled != 0) + mouse.EnableBit((int)offset); + else + mouse.DisableBit((int)offset); + } + + void ProcessEvents() + { + IntPtr root, child; + int root_x, root_y, win_x, win_y; + int buttons; + + using (new XLock(display)) + { + IntPtr window = root_window; + Functions.XQueryPointer(display, window, out root, out child, + out root_x, out root_y, out win_x, out win_y, out buttons); + + if (!mouse_detached) + { + mouse.X = root_x; + mouse.Y = root_y; + } + else + { + mouse.X += (int)root_x - mouse_detached_x; + mouse.Y += (int)root_y - mouse_detached_y; + mouse_detached_x = root_x; + mouse_detached_y = root_y; + } + WriteBit(MouseButton.Left, buttons & (int)MouseMask.Button1Mask); + WriteBit(MouseButton.Middle, buttons & (int)MouseMask.Button2Mask); + WriteBit(MouseButton.Right, buttons & (int)MouseMask.Button3Mask); + // Note: this will never work right, wheel events have a duration of 0 + // (yes, zero). They are impposible to catch via polling. + // After spending a week on this, I simply don't care anymore. + // If someone can fix it, please do. + // Note 2: I have tried passively grabbing those buttons - no go (BadAccess). + // Maybe I am doing something wrong with the grab. + //if ((buttons & (int)MouseMask.Button4Mask) != 0) + // mouse.WheelPrecise++; + //if ((buttons & (int)MouseMask.Button5Mask) != 0) + // mouse.WheelPrecise--; + WriteBit(MouseButton.Button1, buttons & (int)MouseMask.Button6Mask); + WriteBit(MouseButton.Button2, buttons & (int)MouseMask.Button7Mask); + WriteBit(MouseButton.Button3, buttons & (int)MouseMask.Button8Mask); + } + } + } +} + diff --git a/Source/OpenTK/Platform/X11/XI2Mouse.cs b/Source/OpenTK/Platform/X11/XI2Mouse.cs new file mode 100644 index 00000000..1ccf2997 --- /dev/null +++ b/Source/OpenTK/Platform/X11/XI2Mouse.cs @@ -0,0 +1,263 @@ + #region License + // + // The Open Toolkit Library License + // + // Copyright (c) 2006 - 2010 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 + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using OpenTK.Input; + +namespace OpenTK.Platform.X11 +{ + // Todo: multi-mouse support. Right now we aggregate all data into a single mouse device. + // This should be easy: just read the device id and route the data to the correct device. + sealed class XI2Mouse : IMouseDriver2 + { + List mice = new List(); + Dictionary rawids = new Dictionary(); // maps raw ids to mouse ids + internal readonly X11WindowInfo window; + static int XIOpCode; + + static readonly Functions.EventPredicate PredicateImpl = IsEventValid; + readonly IntPtr Predicate = Marshal.GetFunctionPointerForDelegate(PredicateImpl); + + // Store information on a mouse warp event, so it can be ignored. + struct MouseWarp : IEquatable + { + public MouseWarp(double x, double y) { X = x; Y = y; } + double X, Y; + public bool Equals(MouseWarp warp) { return X == warp.X && Y == warp.Y; } + } + MouseWarp? mouse_warp_event; + int mouse_warp_event_count; + + public XI2Mouse() + { + Debug.WriteLine("Using XI2Mouse."); + + using (new XLock(API.DefaultDisplay)) + { + window = new X11WindowInfo(); + window.Display = API.DefaultDisplay; + window.Screen = Functions.XDefaultScreen(window.Display); + window.RootWindow = Functions.XRootWindow(window.Display, window.Screen); + window.WindowHandle = window.RootWindow; + } + + if (!IsSupported(window.Display)) + throw new NotSupportedException("XInput2 not supported."); + + using (XIEventMask mask = new XIEventMask(1, XIEventMasks.RawButtonPressMask | + XIEventMasks.RawButtonReleaseMask | XIEventMasks.RawMotionMask)) + { + Functions.XISelectEvents(window.Display, window.WindowHandle, mask); + Functions.XISelectEvents(window.Display, window.RootWindow, mask); + } + } + + // Checks whether XInput2 is supported on the specified display. + // If a display is not specified, the default display is used. + internal static bool IsSupported(IntPtr display) + { + if (display == IntPtr.Zero) + display = API.DefaultDisplay; + + using (new XLock(display)) + { + int major, ev, error; + if (Functions.XQueryExtension(display, "XInputExtension", out major, out ev, out error) == 0) + { + return false; + } + XIOpCode = major; + } + + return true; + } + + #region IMouseDriver2 Members + + public MouseState GetState() + { + ProcessEvents(); + MouseState master = new MouseState(); + foreach (MouseState ms in mice) + { + master.MergeBits(ms); + } + return master; + } + + public MouseState GetState(int index) + { + ProcessEvents(); + if (mice.Count > index) + return mice[index]; + else + return new MouseState(); + } + + public void SetPosition(double x, double y) + { + using (new XLock(window.Display)) + { + Functions.XWarpPointer(window.Display, + IntPtr.Zero, window.RootWindow, 0, 0, 0, 0, (int)x, (int)y); + + // Mark the expected warp-event so it can be ignored. + if (mouse_warp_event == null) + mouse_warp_event_count = 0; + mouse_warp_event_count++; + mouse_warp_event = new MouseWarp((int)x, (int)y); + } + + ProcessEvents(); + } + + #endregion + + bool CheckMouseWarp(double x, double y) + { + // Check if a mouse warp with the specified destination exists. + bool is_warp = + mouse_warp_event.HasValue && + mouse_warp_event.Value.Equals(new MouseWarp((int)x, (int)y)); + + if (is_warp && --mouse_warp_event_count <= 0) + mouse_warp_event = null; + + return is_warp; + } + + void ProcessEvents() + { + while (true) + { + XEvent e = new XEvent(); + XGenericEventCookie cookie; + + using (new XLock(window.Display)) + { + if (!Functions.XCheckIfEvent(window.Display, ref e, Predicate, new IntPtr(XIOpCode))) + return; + + cookie = e.GenericEventCookie; + if (Functions.XGetEventData(window.Display, ref cookie) != 0) + { + XIRawEvent raw = (XIRawEvent) + Marshal.PtrToStructure(cookie.data, typeof(XIRawEvent)); + + if (!rawids.ContainsKey(raw.deviceid)) + { + mice.Add(new MouseState()); + rawids.Add(raw.deviceid, mice.Count - 1); + } + MouseState state = mice[rawids[raw.deviceid]]; + + switch (raw.evtype) + { + case XIEventType.RawMotion: + double x = 0, y = 0; + if (IsBitSet(raw.valuators.mask, 0)) + { + x = BitConverter.Int64BitsToDouble(Marshal.ReadInt64(raw.raw_values, 0)); + } + if (IsBitSet(raw.valuators.mask, 1)) + { + y = BitConverter.Int64BitsToDouble(Marshal.ReadInt64(raw.raw_values, 8)); + } + + if (!CheckMouseWarp(x, y)) + { + state.X += (int)x; + state.Y += (int)y; + } + break; + + case XIEventType.RawButtonPress: + switch (raw.detail) + { + case 1: state.EnableBit((int)MouseButton.Left); break; + case 2: state.EnableBit((int)MouseButton.Middle); break; + case 3: state.EnableBit((int)MouseButton.Right); break; + case 4: state.WheelPrecise++; break; + case 5: state.WheelPrecise--; break; + case 6: state.EnableBit((int)MouseButton.Button1); break; + case 7: state.EnableBit((int)MouseButton.Button2); break; + case 8: state.EnableBit((int)MouseButton.Button3); break; + case 9: state.EnableBit((int)MouseButton.Button4); break; + case 10: state.EnableBit((int)MouseButton.Button5); break; + case 11: state.EnableBit((int)MouseButton.Button6); break; + case 12: state.EnableBit((int)MouseButton.Button7); break; + case 13: state.EnableBit((int)MouseButton.Button8); break; + case 14: state.EnableBit((int)MouseButton.Button9); break; + } + break; + + case XIEventType.RawButtonRelease: + switch (raw.detail) + { + case 1: state.DisableBit((int)MouseButton.Left); break; + case 2: state.DisableBit((int)MouseButton.Middle); break; + case 3: state.DisableBit((int)MouseButton.Right); break; + case 6: state.DisableBit((int)MouseButton.Button1); break; + case 7: state.DisableBit((int)MouseButton.Button2); break; + case 8: state.DisableBit((int)MouseButton.Button3); break; + case 9: state.DisableBit((int)MouseButton.Button4); break; + case 10: state.DisableBit((int)MouseButton.Button5); break; + case 11: state.DisableBit((int)MouseButton.Button6); break; + case 12: state.DisableBit((int)MouseButton.Button7); break; + case 13: state.DisableBit((int)MouseButton.Button8); break; + case 14: state.DisableBit((int)MouseButton.Button9); break; + } + break; + } + mice[rawids[raw.deviceid]] = state; + } + Functions.XFreeEventData(window.Display, ref cookie); + } + } + } + + static bool IsEventValid(IntPtr display, ref XEvent e, IntPtr arg) + { + return e.GenericEventCookie.extension == arg.ToInt32() && + (e.GenericEventCookie.evtype == (int)XIEventType.RawMotion || + e.GenericEventCookie.evtype == (int)XIEventType.RawButtonPress || + e.GenericEventCookie.evtype == (int)XIEventType.RawButtonRelease); + } + + static bool IsBitSet(IntPtr mask, int bit) + { + unsafe + { + return (*((byte*)mask + (bit >> 3)) & (1 << (bit & 7))) != 0; + } + } + } +} + diff --git a/Source/QuickStart/QuickStart.csproj b/Source/QuickStart/QuickStart.csproj new file mode 100644 index 00000000..969a8d79 --- /dev/null +++ b/Source/QuickStart/QuickStart.csproj @@ -0,0 +1,49 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {90762BBE-CB23-42FF-9D3E-486D2F6CA485} + Exe + QuickStart + QuickStart + + + true + full + false + bin\Debug + DEBUG + prompt + 4 + AnyCPU + false + + + none + false + bin\Release + prompt + 4 + AnyCPU + false + + + + + + + + False + ..\..\Binaries\OpenTK\Release\OpenTK.dll + + + + + OpenTK.dll.config + PreserveNewest + + + \ No newline at end of file