[Linux] Implemented INativeWindow keyboard/mouse events

The mouse cursor is now confined to the display bounds.
This commit is contained in:
thefiddler 2014-07-16 12:18:24 +02:00
parent e61b39a1a1
commit 3881992bf7
4 changed files with 118 additions and 11 deletions

View file

@ -28,6 +28,7 @@
#endregion #endregion
using System; using System;
using System.Diagnostics;
using OpenTK.Input; using OpenTK.Input;
namespace OpenTK.Platform.Linux namespace OpenTK.Platform.Linux
@ -359,10 +360,9 @@ namespace OpenTK.Platform.Linux
return MouseButton.Button8; return MouseButton.Button8;
case EvdevButton.BTN8: case EvdevButton.BTN8:
return MouseButton.Button9; return MouseButton.Button9;
case EvdevButton.BTN9:
return MouseButton.LastButton;
default: default:
return MouseButton.LastButton; Debug.Print("[Input] Unknown EvdevButton {0}", button);
return MouseButton.Left;
} }
} }
} }

View file

@ -258,9 +258,8 @@ namespace OpenTK.Platform.Linux
public Fixed24 DeltaY { get { return GetDY(@event); } } public Fixed24 DeltaY { get { return GetDY(@event); } }
public Fixed24 X { get { return GetAbsX(@event); } } public Fixed24 X { get { return GetAbsX(@event); } }
public Fixed24 Y { get { return GetAbsY(@event); } } public Fixed24 Y { get { return GetAbsY(@event); } }
// Are the following useful? public Fixed24 TransformedX(int width) { return GetAbsXTransformed(@event, width); }
//public Fixed24 TransformedX(int width) { return GetAbsXTransformed(@event, width); } public Fixed24 TransformedY(int height) { return GetAbsYTransformed(@event, height); }
//public Fixed24 TransformedY(int height) { return GetAbsXTransformed(@event, height); }
[DllImport(LibInput.lib, EntryPoint = "libinput_event_pointer_get_time", CallingConvention = CallingConvention.Cdecl)] [DllImport(LibInput.lib, EntryPoint = "libinput_event_pointer_get_time", CallingConvention = CallingConvention.Cdecl)]
static extern uint GetTime(IntPtr @event); static extern uint GetTime(IntPtr @event);

View file

@ -28,7 +28,9 @@
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using OpenTK.Input; using OpenTK.Input;
@ -108,6 +110,9 @@ namespace OpenTK.Platform.Linux
DeviceCollection<KeyboardDevice> Keyboards = new DeviceCollection<KeyboardDevice>(); DeviceCollection<KeyboardDevice> Keyboards = new DeviceCollection<KeyboardDevice>();
DeviceCollection<MouseDevice> Mice = new DeviceCollection<MouseDevice>(); DeviceCollection<MouseDevice> Mice = new DeviceCollection<MouseDevice>();
// Todo: do we need to maintain the geometry of each display separately?
Rectangle bounds;
// Global mouse cursor state // Global mouse cursor state
Vector2 CursorPosition = Vector2.Zero; Vector2 CursorPosition = Vector2.Zero;
// Global mouse cursor offset (used for emulating SetPosition) // Global mouse cursor offset (used for emulating SetPosition)
@ -210,6 +215,12 @@ namespace OpenTK.Platform.Linux
ret < 0 && !(error == ErrorNumber.Again || error == ErrorNumber.Interrupted) || ret < 0 && !(error == ErrorNumber.Again || error == ErrorNumber.Interrupted) ||
(poll_fd.revents & (PollFlags.Hup | PollFlags.Error | PollFlags.Invalid)) != 0; (poll_fd.revents & (PollFlags.Hup | PollFlags.Error | PollFlags.Invalid)) != 0;
// We need to query the desktop bounds in order to position the mouse cursor correctly.
// This value will be used for the current bunch of input events. If a monitor changes
// resolution in the meantime, we might be slightly off in our calculations - this error
// will be corrected when the next bunch of input events arrives.
UpdateDisplayBounds();
if (ret > 0 && (poll_fd.revents & (PollFlags.In | PollFlags.Pri)) != 0) if (ret > 0 && (poll_fd.revents & (PollFlags.In | PollFlags.Pri)) != 0)
{ {
ProcessEvents(input_context); ProcessEvents(input_context);
@ -225,6 +236,19 @@ namespace OpenTK.Platform.Linux
Debug.Print("[Input] Exited input loop.", poll_fd.fd, poll_fd.events); Debug.Print("[Input] Exited input loop.", poll_fd.fd, poll_fd.events);
} }
void UpdateDisplayBounds()
{
bounds = Rectangle.Empty;
for (DisplayIndex i = DisplayIndex.First; i < DisplayIndex.Sixth; i++)
{
DisplayDevice display = DisplayDevice.GetDisplay(i);
if (display != null)
{
bounds = Rectangle.Union(bounds, display.Bounds);
}
}
}
void Setup() void Setup()
{ {
// Todo: add static path fallback when udev is not installed. // Todo: add static path fallback when udev is not installed.
@ -425,16 +449,22 @@ namespace OpenTK.Platform.Linux
{ {
mouse.State.Position += delta; mouse.State.Position += delta;
} }
CursorPosition = new Vector2(
MathHelper.Clamp(CursorPosition.X + delta.X, bounds.Left, bounds.Right),
MathHelper.Clamp(CursorPosition.Y + delta.Y, bounds.Top, bounds.Bottom));
} }
void HandlePointerMotionAbsolute(MouseDevice mouse, PointerEvent e) void HandlePointerMotionAbsolute(MouseDevice mouse, PointerEvent e)
{ {
Vector2 position = new Vector2(e.X, e.Y);
if (mouse != null) if (mouse != null)
{ {
mouse.State.Position = position; mouse.State.Position = new Vector2(e.X, e.Y);
} }
CursorPosition = position; // update global cursor position
CursorPosition = new Vector2(
e.TransformedX(bounds.Width),
e.TransformedY(bounds.Height));
} }
static int GetId(IntPtr device) static int GetId(IntPtr device)

View file

@ -32,6 +32,7 @@ using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using OpenTK.Graphics; using OpenTK.Graphics;
using OpenTK.Input;
using OpenTK.Platform.Egl; using OpenTK.Platform.Egl;
namespace OpenTK.Platform.Linux namespace OpenTK.Platform.Linux
@ -43,9 +44,13 @@ namespace OpenTK.Platform.Linux
LinuxWindowInfo window; LinuxWindowInfo window;
string title; string title;
Icon icon; Icon icon;
bool exists;
Rectangle bounds; Rectangle bounds;
Size client_size; Size client_size;
bool exists;
bool is_focused;
KeyboardState previous_keyboard;
MouseState previous_mouse;
public LinuxNativeWindow(IntPtr display, IntPtr gbm, int fd, public LinuxNativeWindow(IntPtr display, IntPtr gbm, int fd,
int x, int y, int width, int height, string title, int x, int y, int width, int height, string title,
@ -147,10 +152,83 @@ namespace OpenTK.Platform.Linux
return SurfaceFormat.RGBA8888; return SurfaceFormat.RGBA8888;
} }
KeyboardState ProcessKeyboard(KeyboardState keyboard)
{
for (Key i = 0; i < Key.LastKey; i++)
{
if (keyboard[i])
{
OnKeyDown(i, previous_keyboard[i]);
// Todo: implement libxkb-common binding for text input
}
if (!keyboard[i] && previous_keyboard[i])
{
OnKeyUp(i);
}
}
return keyboard;
}
MouseState ProcessMouse(MouseState mouse)
{
for (MouseButton i = 0; i < MouseButton.LastButton; i++)
{
if (mouse[i] && !previous_mouse[i])
{
OnMouseDown(i);
}
if (!mouse[i] && previous_mouse[i])
{
OnMouseUp(i);
}
if (mouse.Position != previous_mouse.Position)
{
OnMouseMove(mouse.X, mouse.Y);
}
if (mouse.Scroll != previous_mouse.Scroll)
{
OnMouseWheel(mouse.Scroll.X, mouse.Scroll.Y);
}
// Note: focus follows mouse. Literally.
bool cursor_in = Bounds.Contains(new Point(mouse.X, mouse.Y));
if (!cursor_in && Focused)
{
OnMouseLeave(EventArgs.Empty);
SetFocus(false);
}
else if (cursor_in && !Focused)
{
OnMouseEnter(EventArgs.Empty);
SetFocus(true);
}
}
return mouse;
}
void SetFocus(bool focus)
{
if (is_focused != focus)
{
is_focused = focus;
OnFocusedChanged(EventArgs.Empty);
}
}
#region INativeWindow Members #region INativeWindow Members
public override void ProcessEvents() public override void ProcessEvents()
{ {
// Note: there is no event-based keyboard/mouse input available.
// We will fake that by polling OpenTK.Input.
previous_keyboard = ProcessKeyboard(Keyboard.GetState());
previous_mouse = ProcessMouse(Mouse.GetCursorState());
base.ProcessEvents(); base.ProcessEvents();
} }
@ -216,7 +294,7 @@ namespace OpenTK.Platform.Linux
{ {
get get
{ {
return true; return is_focused;
} }
} }