[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
using System;
using System.Diagnostics;
using OpenTK.Input;
namespace OpenTK.Platform.Linux
@ -359,10 +360,9 @@ namespace OpenTK.Platform.Linux
return MouseButton.Button8;
case EvdevButton.BTN8:
return MouseButton.Button9;
case EvdevButton.BTN9:
return MouseButton.LastButton;
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 X { get { return GetAbsX(@event); } }
public Fixed24 Y { get { return GetAbsY(@event); } }
// Are the following useful?
//public Fixed24 TransformedX(int width) { return GetAbsXTransformed(@event, width); }
//public Fixed24 TransformedY(int height) { return GetAbsXTransformed(@event, height); }
public Fixed24 TransformedX(int width) { return GetAbsXTransformed(@event, width); }
public Fixed24 TransformedY(int height) { return GetAbsYTransformed(@event, height); }
[DllImport(LibInput.lib, EntryPoint = "libinput_event_pointer_get_time", CallingConvention = CallingConvention.Cdecl)]
static extern uint GetTime(IntPtr @event);

View file

@ -28,7 +28,9 @@
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading;
using OpenTK.Input;
@ -108,6 +110,9 @@ namespace OpenTK.Platform.Linux
DeviceCollection<KeyboardDevice> Keyboards = new DeviceCollection<KeyboardDevice>();
DeviceCollection<MouseDevice> Mice = new DeviceCollection<MouseDevice>();
// Todo: do we need to maintain the geometry of each display separately?
Rectangle bounds;
// Global mouse cursor state
Vector2 CursorPosition = Vector2.Zero;
// Global mouse cursor offset (used for emulating SetPosition)
@ -210,6 +215,12 @@ namespace OpenTK.Platform.Linux
ret < 0 && !(error == ErrorNumber.Again || error == ErrorNumber.Interrupted) ||
(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)
{
ProcessEvents(input_context);
@ -225,6 +236,19 @@ namespace OpenTK.Platform.Linux
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()
{
// Todo: add static path fallback when udev is not installed.
@ -425,16 +449,22 @@ namespace OpenTK.Platform.Linux
{
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)
{
Vector2 position = new Vector2(e.X, e.Y);
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)

View file

@ -32,6 +32,7 @@ using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using OpenTK.Graphics;
using OpenTK.Input;
using OpenTK.Platform.Egl;
namespace OpenTK.Platform.Linux
@ -43,9 +44,13 @@ namespace OpenTK.Platform.Linux
LinuxWindowInfo window;
string title;
Icon icon;
bool exists;
Rectangle bounds;
Size client_size;
bool exists;
bool is_focused;
KeyboardState previous_keyboard;
MouseState previous_mouse;
public LinuxNativeWindow(IntPtr display, IntPtr gbm, int fd,
int x, int y, int width, int height, string title,
@ -147,10 +152,83 @@ namespace OpenTK.Platform.Linux
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
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();
}
@ -216,7 +294,7 @@ namespace OpenTK.Platform.Linux
{
get
{
return true;
return is_focused;
}
}