From 5a2b326a161e9e6444e6513d3e66f188a37c7a85 Mon Sep 17 00:00:00 2001 From: jpbruyere Date: Sat, 13 May 2017 04:10:46 +0200 Subject: [PATCH] work in progress, security commit --- src/Input/KeyModifiers.cs | 8 +- testDrm/Main.cs | 4 +- testDrm/src/Application.cs | 824 +---------------- testDrm/src/BlittableValueType.cs | 2 +- testDrm/src/DRMContext.cs | 832 ++++++++++++++++++ testDrm/src/{ => Linux/Bindings}/Egl.cs | 2 +- testDrm/src/Linux/Bindings/Evdev.cs | 5 +- testDrm/src/Linux/Bindings/LibInput.cs | 2 +- testDrm/src/Linux/Bindings/Libc.cs | 15 +- testDrm/src/Linux/Bindings/Poll.cs | 2 +- testDrm/src/Linux/DRM.cs | 291 ++++++ testDrm/src/Linux/LinuxDisplayDriver.cs | 415 --------- testDrm/src/Linux/LinuxFactory.cs | 253 ------ testDrm/src/Linux/LinuxGraphicsContext.cs | 302 ------- testDrm/src/Linux/LinuxInput.cs | 721 --------------- testDrm/src/Linux/LinuxJoystick.cs | 533 ----------- testDrm/src/Linux/LinuxKeyboardTTY.cs | 268 ------ testDrm/src/Linux/LinuxNativeWindow.cs | 537 ----------- testDrm/src/Linux/Signal.cs | 77 ++ .../src/Linux/{LinuxWindowInfo.cs => TTY.cs} | 46 +- testDrm/src/Linux/VT.cs | 217 +++++ testDrm/testDrm.csproj | 31 +- testDrm/tests.cs | 154 ++++ testDrm/ui/menu.crow | 28 + 24 files changed, 1674 insertions(+), 3895 deletions(-) create mode 100644 testDrm/src/DRMContext.cs rename testDrm/src/{ => Linux/Bindings}/Egl.cs (99%) create mode 100644 testDrm/src/Linux/DRM.cs delete mode 100644 testDrm/src/Linux/LinuxDisplayDriver.cs delete mode 100644 testDrm/src/Linux/LinuxFactory.cs delete mode 100644 testDrm/src/Linux/LinuxGraphicsContext.cs delete mode 100644 testDrm/src/Linux/LinuxInput.cs delete mode 100644 testDrm/src/Linux/LinuxJoystick.cs delete mode 100644 testDrm/src/Linux/LinuxKeyboardTTY.cs delete mode 100644 testDrm/src/Linux/LinuxNativeWindow.cs create mode 100644 testDrm/src/Linux/Signal.cs rename testDrm/src/Linux/{LinuxWindowInfo.cs => TTY.cs} (54%) create mode 100644 testDrm/src/Linux/VT.cs create mode 100644 testDrm/tests.cs create mode 100755 testDrm/ui/menu.crow diff --git a/src/Input/KeyModifiers.cs b/src/Input/KeyModifiers.cs index 1314e737..885365ca 100644 --- a/src/Input/KeyModifiers.cs +++ b/src/Input/KeyModifiers.cs @@ -39,6 +39,7 @@ namespace Crow [Flags] public enum KeyModifiers : byte { + None = 0, /// /// The alt key modifier (option on Mac). /// @@ -52,6 +53,11 @@ namespace Crow /// /// The shift key modifier. /// - Shift = 1 << 2 + Shift = 1 << 2, + + /// + /// The shift key modifier. + /// + AltGr = 1 << 3 } } diff --git a/testDrm/Main.cs b/testDrm/Main.cs index d283e8b1..e8485faf 100644 --- a/testDrm/Main.cs +++ b/testDrm/Main.cs @@ -40,9 +40,7 @@ namespace testDrm using (Application drm = new Application ()) { //drm.CrowInterface.LoadInterface (@"/mnt/data2/devel/crow/Tests/Interfaces/Divers/colorPicker.crow"); - drm.CrowInterface.LoadInterface (@"/mnt/data2/devel/crow/Tests/Interfaces/Divers/0.crow"); - drm.CrowInterface.LoadInterface (@"/mnt/data2/devel/crow/Tests/Interfaces/Divers/0.crow"); - drm.CrowInterface.LoadInterface (@"/mnt/data2/devel/crow/Tests/Interfaces/Divers/0.crow"); + drm.CrowInterface.LoadInterface ("#testDrm.ui.menu.crow"); drm.CrowInterface.LoadInterface (@"/mnt/data2/devel/crow/Tests/Interfaces/Divers/0.crow"); //drm.CrowInterface.LoadInterface (@"/mnt/data2/devel/crow/Tests/Interfaces/GraphicObject/2.crow"); drm.Run (); diff --git a/testDrm/src/Application.cs b/testDrm/src/Application.cs index 57277f9c..63edaae8 100644 --- a/testDrm/src/Application.cs +++ b/testDrm/src/Application.cs @@ -29,7 +29,6 @@ using OpenTK; using System.IO; using System.Collections.Generic; using System.Diagnostics; -using OpenTK.Platform.Egl; using System.Runtime.InteropServices; using System.Threading; @@ -37,7 +36,7 @@ namespace Crow.Linux { public class Application : IDisposable { - #region Crow interface + DRMContext drm; public Interface CrowInterface; public bool mouseIsInInterface = false; @@ -51,831 +50,36 @@ namespace Crow.Linux Thread.Sleep (1); } } - void initCrow (){ + + + public Application(){ + drm = new DRMContext(); CrowInterface = new Interface (); + drm.CrowInterface = CrowInterface; Thread t = new Thread (interfaceThread); t.IsBackground = true; t.Start (); - updateCrowInterfaceBounds (); - } - void updateCrowInterfaceBounds () { - CrowInterface.ProcessResize (new Size (width, height)); - } - #endregion - - volatile bool run = true; - - int fd_gpu = 0; - int major, minor; - - IntPtr gbm_device, gbm_surface, egl_display, egl_config, egl_surface, egl_ctx; - - int r, g, b, a; - - BufferObject cursor_custom; - BufferObject cursor_default; - BufferObject cursor_empty; - - - ModeInfo originalMode, currentMode; - int width, height; - - public IntPtr Connector, Crtc, Encoder; - unsafe ModeCrtc* saved_crtc; - unsafe ModeConnector* pConnector { get { return (ModeConnector*)Connector; } } - unsafe ModeCrtc* pCrtc { get { return (ModeCrtc*)Crtc; } } - unsafe ModeEncoder* pEncoder { get { return (ModeEncoder*)Encoder; } } - - Cairo.EGLDevice cairoDev; - Cairo.GLSurface cairoSurf; - - public Application(string gpu_path = "/dev/dri/card0"){ - DestroyFB = HandleDestroyFB; - DestroyFBPtr = Marshal.GetFunctionPointerForDelegate(DestroyFB); - PageFlip = HandlePageFlip; - PageFlipPtr = Marshal.GetFunctionPointerForDelegate(PageFlip); - - gbm_device = IntPtr.Zero; - egl_display = IntPtr.Zero; - - fd_gpu = Libc.open(gpu_path, OpenFlags.ReadWrite | OpenFlags.CloseOnExec); - if (fd_gpu < 0) - throw new NotSupportedException("[KMS] Failed to open gpu"); - Console.WriteLine("[KMS] GPU '{0}' opened as fd:{1}", gpu_path, fd_gpu); - - initDrm (); - - initGbm (); - - setNewMode (); - - initEgl (); - - initCairo (); - - initInput (); - - initCrow (); - } - - #region init - unsafe void initDrm(){ - ModeRes* resources = (ModeRes*)Drm.ModeGetResources(fd_gpu); - if (resources == null) - throw new NotSupportedException("[KMS] Drm.ModeGetResources failed."); - - ModeConnector* connector = null; - for (int i = 0; i < resources->count_connectors; i++) { - connector = (ModeConnector*)Drm.ModeGetConnector (fd_gpu, *(resources->connectors + i)); - if (connector != null) { - if (connector->connection == ModeConnection.Connected && connector->count_encoders > 0) - break; - Drm.ModeFreeConnector ((IntPtr)connector); - connector = null; - } - } - if (connector == null) - throw new NotSupportedException("[KMS] No connected screen found"); - - Connector = (IntPtr)connector; - Encoder = Drm.ModeGetEncoder (fd_gpu, connector->encoder_id); - Crtc = Drm.ModeGetCrtc(fd_gpu, pEncoder->crtc_id); - saved_crtc = (ModeCrtc*) Drm.ModeGetCrtc (fd_gpu, pEncoder->crtc_id); - - originalMode = pCrtc->mode; - width = 1600; - height = 900; - -// Console.WriteLine ("[DRM]: current mode = {0} X {1} at {2} Hz", width, height, currentMode.vrefresh); - } - void initGbm (){ - gbm_device = Gbm.CreateDevice(fd_gpu); - if (gbm_device == IntPtr.Zero) - throw new NotSupportedException("[GBM] Failed to create GBM device"); - - gbm_surface = Gbm.CreateSurface(gbm_device, width, height, SurfaceFormat.ARGB8888, SurfaceFlags.Rendering | SurfaceFlags.Scanout); - if (gbm_surface == IntPtr.Zero) - throw new NotSupportedException("[GBM] Failed to create GBM surface for rendering"); - } - - void setNewMode (){ - //118.25 1600 1696 1856 2112 900 903 908 934 -hsync +vsync - //currentMode = pCrtc->mode; - currentMode.clock = 118250; - currentMode.hdisplay = 1600; - currentMode.hsync_start = 1696; - currentMode.hsync_end = 1856; - currentMode.htotal = 2112; - currentMode.vdisplay = 900; - currentMode.vsync_start = 903; - currentMode.vsync_end = 908; - currentMode.vtotal = 934; - currentMode.flags |= (uint)ModeFlags.NHSYNC; - currentMode.flags |= (uint)ModeFlags.PVSYNC; - // byte[] tmp = System.Text.Encoding.ASCII.GetBytes ("1600x900"); - // for (int i = 0; i < tmp.Length; i++) { - // currentMode.name [i] = (sbyte)tmp [i]; - // } - - unsafe - { - pCrtc->mode = currentMode; - ModeInfo* mode = (ModeInfo*)Marshal.AllocHGlobal (sizeof(ModeInfo));// pConnector->modes; - *mode = currentMode; - int connector_id = pConnector->connector_id; - int crtc_id = pEncoder->crtc_id; - BufferObject bo = Gbm.CreateBuffer( - gbm_device, width, height, SurfaceFormat.ARGB8888, SurfaceFlags.Scanout); - int nfb = getFbFromBo (bo); - int ret = Drm.ModeSetCrtc (fd_gpu, crtc_id, nfb, 0, 0, &connector_id, 1, mode); - - if (ret != 0) - Console.WriteLine("[KMS] Drm.ModeSetCrtc failed. Error: " + ret); - } -// width = currentMode.hdisplay; -// height = currentMode.vdisplay; - } - - unsafe void initEgl () { - IntPtr[] configs = new IntPtr[1]; - int[] contextAttrib = new int[] { - Egl.CONTEXT_CLIENT_VERSION, 2, - Egl.NONE - }; - int[] attribList = new int[] - { - Egl.SURFACE_TYPE, Egl.WINDOW_BIT, - Egl.RENDERABLE_TYPE, Egl.OPENGL_BIT, - Egl.RED_SIZE, 1, - Egl.GREEN_SIZE, 1, - Egl.BLUE_SIZE, 1, - Egl.ALPHA_SIZE, 0, - - //Egl.DEPTH_SIZE, 24, - //Egl.STENCIL_SIZE, 0, - - //Egl.SAMPLE_BUFFERS, 2, - //Egl.SAMPLES, 0, - Egl.NONE - }; - int num_configs; - - egl_display = Egl.GetDisplay(gbm_device); - if (egl_display == IntPtr.Zero) - throw new NotSupportedException("[KMS] Failed to create EGL display"); - Console.WriteLine("[EGL] EGL display {0:x} created successfully", egl_display); - - if (!Egl.Initialize(egl_display, out major, out minor)) - throw new NotSupportedException("[EGL] Failed to initialize EGL display. Error code: " + Egl.GetError()); - - if (!Egl.BindAPI (RenderApi.GL)) - throw new NotSupportedException("[EGL] Failed to bind EGL Api: " + Egl.GetError()); - - Console.WriteLine ("[EGL] Version: " + Marshal.PtrToStringAuto (Egl.QueryString (egl_display, Egl.VERSION))); - Console.WriteLine ("[EGL] Vendor: " + Marshal.PtrToStringAuto (Egl.QueryString (egl_display, Egl.VENDOR))); - Console.WriteLine ("[EGL] Extensions: " + Marshal.PtrToStringAuto (Egl.QueryString (egl_display, Egl.EXTENSIONS))); - Console.WriteLine ("[EGL] Extensions: " + Marshal.PtrToStringAuto (Egl.QueryString(IntPtr.Zero, Egl.EXTENSIONS))); - - if (!Egl.ChooseConfig(egl_display, attribList, configs, configs.Length, out num_configs) || num_configs == 0) - throw new NotSupportedException(String.Format("Failed to retrieve GraphicsMode, error {0}", Egl.GetError())); - - - // See what we really got - int d, s, sample_buffers, samples; - IntPtr active_config = configs[0]; - Egl.GetConfigAttrib(egl_display, active_config, Egl.RED_SIZE, out r); - Egl.GetConfigAttrib(egl_display, active_config, Egl.GREEN_SIZE, out g); - Egl.GetConfigAttrib(egl_display, active_config, Egl.BLUE_SIZE, out b); - Egl.GetConfigAttrib(egl_display, active_config, Egl.ALPHA_SIZE, out a); - //Egl.GetConfigAttrib(egl_display, active_config, Egl.DEPTH_SIZE, out d); - //Egl.GetConfigAttrib(egl_display, active_config, Egl.STENCIL_SIZE, out s); - //Egl.GetConfigAttrib(egl_display, active_config, Egl.SAMPLE_BUFFERS, out sample_buffers); - //Egl.GetConfigAttrib(egl_display, active_config, Egl.SAMPLES, out samples); -// Console.WriteLine ("EGL context: {0},{1},{2},{3} depth={4} stencil={5} samples={6} sample buffers={7}", -// r, g, b, a, d, s, samples, sample_buffers); - egl_config = active_config; - - egl_ctx = Egl.CreateContext(egl_display, egl_config, IntPtr.Zero, contextAttrib); - - egl_surface = Egl.CreateWindowSurface(egl_display, egl_config, gbm_surface, IntPtr.Zero); - - if (egl_surface==IntPtr.Zero) - throw new NotSupportedException(String.Format("[EGL] Failed to create window surface, error {0}.", Egl.GetError())); - - if (!Egl.MakeCurrent(egl_display, egl_surface, egl_surface, egl_ctx)) - throw new NotSupportedException(string.Format("Failed to make context {0} current. Error: {1}", gbm_surface, Egl.GetError())); - - cursor_default = CreateCursor(gbm_device, Cursors.Default); - cursor_empty = CreateCursor(gbm_device, Cursors.Empty); - - SetCursor(Cursors.Default); - unsafe { - Drm.MoveCursor (fd_gpu, pEncoder->crtc_id, 0, 0); - } - } - - void initCairo (){ - cairoDev = new Cairo.EGLDevice (egl_display, egl_ctx); - - cairoSurf = new Cairo.GLSurface (cairoDev, egl_surface, width, height); - //cairoSurf = new Cairo.EGLSurface (cairoDev, egl_surface, 1600, 900); - - cairoDev.SetThreadAware (false); - - if (cairoDev.Acquire () != Cairo.Status.Success) - Console.WriteLine ("[Cairo]: Failed to acquire egl device."); - } - #endregion - - #region cursor - - static BufferObject CreateCursor(IntPtr gbm, MouseCursor cursor) - { - if (cursor.Width > 64 || cursor.Height > 64) - { - Debug.Print("[KMS] Cursor size {0}x{1} unsupported. Maximum is 64x64.", - cursor.Width, cursor.Height); - return default(BufferObject); - } - - int width = 64; - int height = 64; - SurfaceFormat format = SurfaceFormat.ARGB8888; - SurfaceFlags usage = SurfaceFlags.Cursor64x64 | SurfaceFlags.Write; - - Debug.Print("[KMS] Gbm.CreateBuffer({0:X}, {1}, {2}, {3}, {4}).", - gbm, width, height, format, usage); - - BufferObject bo = Gbm.CreateBuffer( - gbm, width, height, format, usage); - - if (bo == BufferObject.Zero) - { - Debug.Print("[KMS] Failed to create buffer."); - return bo; - } - - // Copy cursor.Data into a new buffer of the correct size - byte[] cursor_data = new byte[width * height * 4]; - for (int y = 0; y < cursor.Height; y++) - { - int dst_offset = y * width * 4; - int src_offset = y * cursor.Width * 4; - int src_length = cursor.Width * 4; - Array.Copy( - cursor.Data, src_offset, - cursor_data, dst_offset, - src_length); - } - bo.Write(cursor_data); - - return bo; - } - void SetCursor(MouseCursor cursor) - { - BufferObject bo = default(BufferObject); - if (cursor == MouseCursor.Default) - { - bo = cursor_default; - } - else if (cursor == MouseCursor.Empty) - { - bo = cursor_empty; - } - else - { - if (cursor_custom != BufferObject.Zero) - cursor_custom.Dispose(); - cursor_custom = CreateCursor(gbm_device, cursor); - bo = cursor_custom; - } - - // If we failed to create a proper cursor, try falling back - // to the empty cursor. We do not want to crash here! - if (bo == BufferObject.Zero) - { - bo = cursor_empty; - } - - if (bo != BufferObject.Zero) - { - unsafe { - Console.WriteLine ("Cursor ({0},{1})", cursor.X, cursor.Y); - Drm.SetCursor (fd_gpu, pEncoder->crtc_id, - bo.Handle, bo.Width, bo.Height, cursor.X, cursor.Y); - } - } - } - #endregion - - int getFbFromBo (BufferObject bo){ - int width = bo.Width; - int height = bo.Height; - int bpp = 32; - int depth = 24; - int stride = bo.Stride; - int hndBO = bo.Handle; - - int fb; - int ret = Drm.ModeAddFB (fd_gpu, width, height,(byte)depth, (byte)bpp, stride, hndBO, out fb); - if (ret != 0) - throw new Exception ("[DRM]: ModeAddFB failed."); - bo.SetUserData ((IntPtr)fb, DestroyFBPtr); - return fb; - } - - public void Run(){ - BufferObject bo; - int fb; - - PollFD fds = new PollFD(); - fds.fd = fd_gpu; - fds.events = PollFlags.In; - - EventContext evctx = new EventContext(); - evctx.version = EventContext.Version; - evctx.page_flip_handler = PageFlipPtr; - - int timeout = -1;//block ? -1 : 0; - - using (Cairo.Context ctx = new Cairo.Context (cairoSurf)) { - ctx.Rectangle (0, 0, width, height); - ctx.SetSourceRGB (0, 0, 0); - ctx.Fill (); - } - - cairoSurf.SwapBuffers (); - - bo = Gbm.LockFrontBuffer (gbm_surface); - fb = getFbFromBo (bo); - - //SetScanoutRegion (fb); - - while (run){ - BufferObject next_bo; - bool update = false; - - if (updateMousePos) { - lock (Sync) { - updateMousePos = false; - unsafe { - Drm.MoveCursor (fd_gpu, pEncoder->crtc_id, MouseX-8, MouseY-4); - } - } - } - - if (Monitor.TryEnter (CrowInterface.RenderMutex)) { - if (CrowInterface.IsDirty) { - CrowInterface.IsDirty = false; - update = true; - using (Cairo.Context ctx = new Cairo.Context (cairoSurf)) { - using (Cairo.Surface d = new Cairo.ImageSurface (CrowInterface.dirtyBmp, Cairo.Format.Argb32, - width, height, width * 4)) { - ctx.SetSourceSurface (d, 0, 0); - ctx.Operator = Cairo.Operator.Source; - ctx.Paint (); - } - } - } - Monitor.Exit (CrowInterface.RenderMutex); - } - - if (!update) - continue; - update = false; - - cairoSurf.Flush (); - cairoSurf.SwapBuffers (); - - if (Gbm.HasFreeBuffers (gbm_surface) == 0) - throw new Exception ("[GBM]: Out of free buffers."); - - next_bo = Gbm.LockFrontBuffer (gbm_surface); - if (next_bo == BufferObject.Zero) - throw new Exception ("[GBM]: Failed to lock front buffer."); + CrowInterface.ProcessResize (new Size (drm.width, drm.height)); + drm.updateCursor (XCursor.Default); - fb = getFbFromBo (next_bo); - - unsafe{ - int is_flip_queued = 1; - - while (Drm.ModePageFlip (fd_gpu, pEncoder->crtc_id, fb, PageFlipFlags.FlipEvent, ref is_flip_queued) < 0) { - //Console.WriteLine ("[DRM] Failed to enqueue framebuffer flip."); - continue; - } - - while (is_flip_queued != 0) - { - fds.revents = 0; - if (Libc.poll (ref fds, 1, timeout) < 0) - break; - - if ((fds.revents & (PollFlags.Hup | PollFlags.Error)) != 0) - break; - - if ((fds.revents & PollFlags.In) != 0) - Drm.HandleEvent (fd_gpu, ref evctx); - else - break; - } - if (is_flip_queued != 0) - Console.WriteLine ("flip canceled"); - - Gbm.ReleaseBuffer (gbm_surface, bo); - //Drm.ModeRmFB(fd_gpu, fb); - - bo = next_bo; - next_bo = BufferObject.Zero; - - } - } - } - - #region rendering - - - - // We only support a SwapInterval of 0 (immediate) - // or 1 (vsynced). - // Todo: add support for SwapInterval of -1 (adaptive). - // This requires a small change in WaitFlip(). - int swap_interval=0; - - readonly IntPtr PageFlipPtr; - readonly PageFlipCallback PageFlip; - - readonly IntPtr DestroyFBPtr; - readonly DestroyUserDataCallback DestroyFB; - - void HandlePageFlip(int fd, int sequence, int tv_sec, int tv_usec, ref int user_data) - { - user_data = 0; + CrowInterface.MouseCursorChanged += CrowInterface_MouseCursorChanged; } - - void HandleDestroyFB(BufferObject bo, IntPtr data) + void CrowInterface_MouseCursorChanged (object sender, MouseCursorChangedEventArgs e) { - Console.WriteLine ("DestroyFB"); - IntPtr gbm = bo.Device; - int fb = data.ToInt32(); - - if (fb != 0) - Drm.ModeRmFB(fd_gpu, fb); + drm.updateCursor (e.NewCursor); } - void SetScanoutRegion(int buffer) - { - unsafe - { - ModeInfo* mode = pConnector->modes; - int connector_id = pConnector->connector_id; - int crtc_id = pEncoder->crtc_id; - - int ret = Drm.ModeSetCrtc (fd_gpu, crtc_id, buffer, 0, 0, &connector_id, 1, mode); - - if (ret != 0) - Debug.Print("[KMS] Drm.ModeSetCrtc{0}, {1}, {2} failed. Error: {3}", - fd_gpu, crtc_id, buffer, ret); - Console.WriteLine ("scanout region set: {0}x{1}", mode->hdisplay, mode->vdisplay); - } + public void Run (){ + drm.Run (); } - #endregion #region IDisposable implementation public void Dispose () { - cairoDev.Release (); - cairoSurf.Dispose (); - cairoDev.Dispose (); - -// if (fb != 0) -// Drm.ModeRmFB (fd_gpu, fb); -// if (bo != BufferObject.Zero) -// Gbm.ReleaseBuffer (gbm_surface, bo); -// if (next_fb != 0) -// Drm.ModeRmFB (fd_gpu, next_fb); -// if (next_bo != BufferObject.Zero) -// Gbm.ReleaseBuffer (gbm_surface, next_bo); - - if (Egl.GetCurrentContext () == egl_ctx) { - Console.WriteLine ("destroying context"); - Egl.DestroyContext (egl_display, egl_ctx); - }else - Console.WriteLine ("not current"); - - cairoDev.Dispose (); - - unsafe{ - Drm.ModeSetCrtc (fd_gpu, saved_crtc->crtc_id, saved_crtc->buffer_id, - saved_crtc->x, saved_crtc->y, &pConnector->connector_id, 1, &saved_crtc->mode); - Drm.ModeFreeCrtc ((IntPtr)saved_crtc); - } - - Drm.ModeFreeCrtc (Crtc); - Drm.ModeFreeConnector(Connector); - Drm.ModeFreeEncoder(Encoder); - Libc.close(fd_gpu); - } - #endregion - - #region tests - unsafe void dumpDrmResources(){ - ModeRes* resources = (ModeRes*)Drm.ModeGetResources(fd_gpu); - if (resources == null) - throw new NotSupportedException("[KMS] Drm.ModeGetResources failed."); - Console.WriteLine("[KMS] DRM found {0} connectors", resources->count_connectors); - - Console.WriteLine ("[ENCODERS]"); - for (int j = 0; j < resources->count_encoders; j++) { - ModeEncoder* e = (ModeEncoder*)Drm.ModeGetEncoder(fd_gpu, *(resources->encoders + j)); - - if (e == null) - continue; - Console.WriteLine ("{0}\t{1}\t{2}",e->encoder_id, e->encoder_type, e->crtc_id); - - Drm.ModeFreeEncoder((IntPtr)e); - } - - Console.WriteLine ("\n[CONNECTORS]"); - ModeConnector* connector = null; - for (int i = 0; i < resources->count_connectors; i++) - { - connector = (ModeConnector*)Drm.ModeGetConnector(fd_gpu, *(resources->connectors + i)); - if (connector != null) - { - Console.WriteLine ("{0}\t{1}\t{2}\t{3}", - connector->connector_id, - connector->connector_type, - connector->connection, - connector->encoder_id); - - for (int j = 0; j < connector->count_modes; j++) { - ModeInfo* mode = connector->modes + j; - if (mode == null) - continue; - Console.WriteLine ("\t{0,-20}{1,5}{2,5}{3,4} hz", - new string(mode->name), - mode->hdisplay, - mode->vdisplay, - mode->vrefresh); - } - Drm.ModeFreeConnector((IntPtr)connector); - connector = null; - } - } - } - #endregion - - #region INPUT - Thread input_thread; - long exit; - - static readonly object Sync = new object(); - static readonly Crow.Key[] KeyMap = Evdev.KeyMap; - static long DeviceFDCount; - - IntPtr udev; - IntPtr input_context; - - int input_fd = 0; - - InputInterface input_interface = new InputInterface( - OpenRestricted, CloseRestricted); - static CloseRestrictedCallback CloseRestricted = CloseRestrictedHandler; - static void CloseRestrictedHandler(int fd, IntPtr data) - { - Debug.Print("[Input] Closing fd {0}", fd); - int ret = Libc.close(fd); - - if (ret < 0) - { - Debug.Print("[Input] Failed to close fd {0}. Error: {1}", fd, ret); - } - else - { - Interlocked.Decrement(ref DeviceFDCount); - } - } - - static OpenRestrictedCallback OpenRestricted = OpenRestrictedHandler; - static int OpenRestrictedHandler(IntPtr path, int flags, IntPtr data) - { - int fd = Libc.open(path, (OpenFlags)flags); - Debug.Print("[Input] Opening '{0}' with flags {1}. fd:{2}", - Marshal.PtrToStringAnsi(path), (OpenFlags)flags, fd); - - if (fd >= 0) - { - Interlocked.Increment(ref DeviceFDCount); - } - - return fd; - } - - void initInput (){ - Semaphore ready = new Semaphore(0, 1); - input_thread = new Thread (InputThreadLoop); - input_thread.IsBackground = true; - input_thread.Start(ready); - } - - void InputThreadLoop(object semaphore) - { - Debug.Print("[Input] Running on thread {0}", Thread.CurrentThread.ManagedThreadId); - Setup(); - - // Inform the parent thread that initialization has completed successfully - (semaphore as Semaphore).Release(); - Debug.Print("[Input] Released main thread.", input_context); - - // Use a blocking poll for input messages, in order to reduce CPU usage - PollFD poll_fd = new PollFD(); - poll_fd.fd = input_fd; - poll_fd.events = PollFlags.In; - Debug.Print("[Input] Created PollFD({0}, {1})", poll_fd.fd, poll_fd.events); - - Debug.Print("[Input] Entering input loop.", poll_fd.fd, poll_fd.events); - while (Interlocked.Read(ref exit) == 0) - { - int ret = Libc.poll(ref poll_fd, 1, -1); - ErrorNumber error = (ErrorNumber)Marshal.GetLastWin32Error(); - bool is_error = - ret < 0 && !(error == ErrorNumber.Again || error == ErrorNumber.Interrupted) || - (poll_fd.revents & (PollFlags.Hup | PollFlags.Error | PollFlags.Invalid)) != 0; - - if (ret > 0 && (poll_fd.revents & (PollFlags.In | PollFlags.Pri)) != 0) - ProcessEvents(input_context); - - if (is_error) - { - Debug.Print("[Input] Exiting input loop {0} due to poll error [ret:{1} events:{2}]. Error: {3}.", - input_thread.ManagedThreadId, ret, poll_fd.revents, error); - Interlocked.Increment(ref exit); - } - } - Debug.Print("[Input] Exited input loop.", poll_fd.fd, poll_fd.events); - } - - void Setup() - { - // Todo: add static path fallback when udev is not installed. - udev = Udev.New(); - if (udev == IntPtr.Zero) - { - Debug.Print("[Input] Udev.New() failed."); - Interlocked.Increment(ref exit); - return; - } - Debug.Print("[Input] Udev.New() = {0:x}", udev); - - input_context = LibInput.CreateContext(input_interface, IntPtr.Zero, udev); - if (input_context == IntPtr.Zero) - { - Debug.Print("[Input] LibInput.CreateContext({0:x}) failed.", udev); - Interlocked.Increment(ref exit); - return; - } - Debug.Print("[Input] LibInput.CreateContext({0:x}) = {1:x}", udev, input_context); - - string seat_id = "seat0"; - int seat_assignment = LibInput.AssignSeat(input_context, seat_id); - if (seat_assignment == -1) - { - Debug.Print("[Input] LibInput.AssignSeat({0:x}) = {1} failed.", input_context, seat_id); - Interlocked.Increment(ref exit); - return; - } - Debug.Print("[Input] LibInput.AssignSeat({0:x}) = {1}", input_context, seat_id); - - input_fd = LibInput.GetFD(input_context); - if (input_fd < 0) - { - Debug.Print("[Input] LibInput.GetFD({0:x}) failed.", input_context); - Interlocked.Increment(ref exit); - return; - } - Debug.Print("[Input] LibInput.GetFD({0:x}) = {1}.", input_context, input_fd); - - ProcessEvents(input_context); - LibInput.Resume(input_context); - Debug.Print("[Input] LibInput.Resume({0:x})", input_context); - - if (Interlocked.Read(ref DeviceFDCount) <= 0) - { - Debug.Print("[Error] Failed to open any input devices."); - Debug.Print("[Error] Ensure that you have access to '/dev/input/event*'."); - Interlocked.Increment(ref exit); - } - } - - void ProcessEvents(IntPtr input_context) - { - // Process all events in the event queue - while (true) - { - // Data available - int ret = LibInput.Dispatch(input_context); - if (ret != 0) - { - Debug.Print("[Input] LibInput.Dispatch({0:x}) failed. Error: {1}", - input_context, ret); - break; - } - - IntPtr pevent = LibInput.GetEvent(input_context); - if (pevent == IntPtr.Zero) - { - break; - } - - IntPtr device = LibInput.GetDevice(pevent); - InputEventType type = LibInput.GetEventType(pevent); - - lock (Sync) - { - switch (type) - { -// case InputEventType.DeviceAdded: -// HandleDeviceAdded(input_context, device); -// break; -// -// case InputEventType.DeviceRemoved: -// HandleDeviceRemoved(input_context, device); -// break; -// - case InputEventType.KeyboardKey: - run = false; - //HandleKeyboard(GetKeyboard(device), LibInput.GetKeyboardEvent(pevent)); - break; -// -// case InputEventType.PointerAxis: -// HandlePointerAxis(GetMouse(device), LibInput.GetPointerEvent(pevent)); -// break; -// - case InputEventType.PointerButton: - handlePointerButton (LibInput.GetPointerEvent(pevent)); - break; - - case InputEventType.PointerMotion: - handlePointerMotion (LibInput.GetPointerEvent(pevent)); - break; - -// case InputEventType.PointerMotionAbsolute: -// HandlePointerMotionAbsolute(GetMouse(device), LibInput.GetPointerEvent(pevent)); -// break; - } - } - - LibInput.DestroyEvent(pevent); - } - } - int MouseX = 0, MouseY = 0; - volatile bool updateMousePos = true; - - int roundDelta (double d){ - return d > 0 ? (int)Math.Ceiling(d) : (int)Math.Floor (d); - } - - void handlePointerMotion(PointerEvent e) - { - MouseX += roundDelta (e.DeltaX); - MouseY += roundDelta (e.DeltaY); - - Rectangle bounds = CrowInterface.ClientRectangle; - if (MouseX < bounds.Left) - MouseX = bounds.Left; - else if (MouseX > bounds.Right) - MouseX = bounds.Right; - - if (MouseY < bounds.Top) - MouseY = bounds.Top; - else if (MouseY > bounds.Bottom) - MouseY = bounds.Bottom; - - CrowInterface.ProcessMouseMove (MouseX, MouseY); - - updateMousePos = true; - } - void handlePointerButton (PointerEvent e) - { - int but = 0; - switch (e.Button) { - case EvdevButton.LEFT: - but = 0; - break; - case EvdevButton.MIDDLE: - but = 1; - break; - case EvdevButton.RIGHT: - but = 2; - break; - } - if (e.ButtonState == OpenTK.Platform.Linux.ButtonState.Pressed) - CrowInterface.ProcessMouseButtonDown (but); - else - CrowInterface.ProcessMouseButtonUp (but); + drm.Dispose (); } #endregion } diff --git a/testDrm/src/BlittableValueType.cs b/testDrm/src/BlittableValueType.cs index 1c3e1213..00595179 100644 --- a/testDrm/src/BlittableValueType.cs +++ b/testDrm/src/BlittableValueType.cs @@ -32,7 +32,7 @@ using System.Runtime.InteropServices; using System.Diagnostics; using System.Reflection; -namespace OpenTK +namespace Crow { #region BlittableValueType diff --git a/testDrm/src/DRMContext.cs b/testDrm/src/DRMContext.cs new file mode 100644 index 00000000..720d4ed9 --- /dev/null +++ b/testDrm/src/DRMContext.cs @@ -0,0 +1,832 @@ +// +// DRMDevice.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.IO; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using OpenTK; +using OpenTK.Platform.Linux; +using System.IO.Compression; + +namespace Crow.Linux +{ + + internal class DRMContext : IDisposable + { + Stopwatch drmTimeOut; + + public Interface CrowInterface; + int fd_gpu = 0; + + IntPtr gbm_device, gbm_surface, egl_display, egl_config, egl_surface, egl_ctx; + + ModeInfo originalMode, currentMode; + public int width, height; + + volatile bool run = true; + + public IntPtr Connector, Crtc, Encoder; + unsafe ModeCrtc* saved_crtc; + unsafe ModeConnector* pConnector { get { return (ModeConnector*)Connector; } } + unsafe ModeCrtc* pCrtc { get { return (ModeCrtc*)Crtc; } } + unsafe ModeEncoder* pEncoder { get { return (ModeEncoder*)Encoder; } } + + Cairo.EGLDevice cairoDev; + Cairo.GLSurface cairoSurf; + + Dictionary keymap; + + int previousVT = 0, appVT; + + public DRMContext(string gpu_path = "/dev/dri/card0"){ + drmTimeOut = new Stopwatch(); + + DestroyFB = HandleDestroyFB; + DestroyFBPtr = Marshal.GetFunctionPointerForDelegate(DestroyFB); + PageFlip = HandlePageFlip; + PageFlipPtr = Marshal.GetFunctionPointerForDelegate(PageFlip); + + gbm_device = IntPtr.Zero; + egl_display = IntPtr.Zero; + + fd_gpu = Libc.open(gpu_path, OpenFlags.ReadWrite | OpenFlags.CloseOnExec); + if (fd_gpu < 0) + throw new NotSupportedException("[KMS] Failed to open gpu"); + Console.WriteLine("[KMS] GPU '{0}' opened as fd:{1}", gpu_path, fd_gpu); + + initDrm (); + + initGbm (); + + setNewMode (); + + initEgl (); + + initCairo (); + + initInput (); + } + + #region init + unsafe void initDrm(){ + ModeRes* resources = (ModeRes*)Drm.ModeGetResources(fd_gpu); + if (resources == null) + throw new NotSupportedException("[KMS] Drm.ModeGetResources failed."); + + ModeConnector* connector = null; + for (int i = 0; i < resources->count_connectors; i++) { + connector = (ModeConnector*)Drm.ModeGetConnector (fd_gpu, *(resources->connectors + i)); + if (connector != null) { + if (connector->connection == ModeConnection.Connected && connector->count_encoders > 0) + break; + Drm.ModeFreeConnector ((IntPtr)connector); + connector = null; + } + } + if (connector == null) + throw new NotSupportedException("[KMS] No connected screen found"); + + Connector = (IntPtr)connector; + Encoder = Drm.ModeGetEncoder (fd_gpu, connector->encoder_id); + Crtc = Drm.ModeGetCrtc(fd_gpu, pEncoder->crtc_id); + saved_crtc = (ModeCrtc*) Drm.ModeGetCrtc (fd_gpu, pEncoder->crtc_id); + + originalMode = pCrtc->mode; + width = 1600; + height = 900; + + // Console.WriteLine ("[DRM]: current mode = {0} X {1} at {2} Hz", width, height, currentMode.vrefresh); + } + void initGbm (){ + gbm_device = Gbm.CreateDevice(fd_gpu); + if (gbm_device == IntPtr.Zero) + throw new NotSupportedException("[GBM] Failed to create GBM device"); + + gbm_surface = Gbm.CreateSurface(gbm_device, width, height, SurfaceFormat.ARGB8888, SurfaceFlags.Rendering | SurfaceFlags.Scanout); + if (gbm_surface == IntPtr.Zero) + throw new NotSupportedException("[GBM] Failed to create GBM surface for rendering"); + } + + void setNewMode (){ + //118.25 1600 1696 1856 2112 900 903 908 934 -hsync +vsync + //currentMode = pCrtc->mode; + currentMode.clock = 118250; + currentMode.hdisplay = 1600; + currentMode.hsync_start = 1696; + currentMode.hsync_end = 1856; + currentMode.htotal = 2112; + currentMode.vdisplay = 900; + currentMode.vsync_start = 903; + currentMode.vsync_end = 908; + currentMode.vtotal = 934; + currentMode.flags |= (uint)ModeFlags.NHSYNC; + currentMode.flags |= (uint)ModeFlags.PVSYNC; + // byte[] tmp = System.Text.Encoding.ASCII.GetBytes ("1600x900"); + // for (int i = 0; i < tmp.Length; i++) { + // currentMode.name [i] = (sbyte)tmp [i]; + // } + + unsafe + { + pCrtc->mode = currentMode; + ModeInfo* mode = (ModeInfo*)Marshal.AllocHGlobal (sizeof(ModeInfo));// pConnector->modes; + *mode = currentMode; + int connector_id = pConnector->connector_id; + int crtc_id = pEncoder->crtc_id; + BufferObject bo = Gbm.CreateBuffer( + gbm_device, width, height, SurfaceFormat.ARGB8888, SurfaceFlags.Scanout); + int nfb = getFbFromBo (bo); + int ret = Drm.ModeSetCrtc (fd_gpu, crtc_id, nfb, 0, 0, &connector_id, 1, mode); + + if (ret != 0) + Console.WriteLine("[KMS] Drm.ModeSetCrtc failed. Error: " + ret); + } + // width = currentMode.hdisplay; + // height = currentMode.vdisplay; + } + + unsafe void initEgl () { + int major, minor; + IntPtr[] configs = new IntPtr[1]; + int[] contextAttrib = new int[] { + Egl.CONTEXT_CLIENT_VERSION, 2, + Egl.NONE + }; + int[] attribList = new int[] + { + Egl.SURFACE_TYPE, Egl.WINDOW_BIT, + Egl.RENDERABLE_TYPE, Egl.OPENGL_BIT, + Egl.RED_SIZE, 1, + Egl.GREEN_SIZE, 1, + Egl.BLUE_SIZE, 1, + Egl.ALPHA_SIZE, 0, + //Egl.DEPTH_SIZE, 24, + //Egl.STENCIL_SIZE, 0, + //Egl.SAMPLE_BUFFERS, 2, + //Egl.SAMPLES, 0, + Egl.NONE + }; + int num_configs; + + egl_display = Egl.GetDisplay(gbm_device); + + if (egl_display == IntPtr.Zero) + throw new NotSupportedException("[KMS] Failed to create EGL display"); + Console.WriteLine("[EGL] EGL display {0:x} created successfully", egl_display); + + if (!Egl.Initialize(egl_display, out major, out minor)) + throw new NotSupportedException("[EGL] Failed to initialize EGL display. Error code: " + Egl.GetError()); + + if (!Egl.BindAPI (RenderApi.GL)) + throw new NotSupportedException("[EGL] Failed to bind EGL Api: " + Egl.GetError()); + + Console.WriteLine ("[EGL] Version: " + Marshal.PtrToStringAuto (Egl.QueryString (egl_display, Egl.VERSION))); + Console.WriteLine ("[EGL] Vendor: " + Marshal.PtrToStringAuto (Egl.QueryString (egl_display, Egl.VENDOR))); + Console.WriteLine ("[EGL] Extensions: " + Marshal.PtrToStringAuto (Egl.QueryString (egl_display, Egl.EXTENSIONS))); + Console.WriteLine (" " + Marshal.PtrToStringAuto (Egl.QueryString(IntPtr.Zero, Egl.EXTENSIONS))); + + if (!Egl.ChooseConfig(egl_display, attribList, configs, configs.Length, out num_configs) || num_configs == 0) + throw new NotSupportedException(String.Format("Failed to retrieve GraphicsMode, error {0}", Egl.GetError())); + + egl_config = configs[0]; + egl_ctx = Egl.CreateContext(egl_display, egl_config, IntPtr.Zero, contextAttrib); + egl_surface = Egl.CreateWindowSurface(egl_display, egl_config, gbm_surface, IntPtr.Zero); + + if (egl_surface==IntPtr.Zero) + throw new NotSupportedException(String.Format("[EGL] Failed to create window surface, error {0}.", Egl.GetError())); + + // See what we really got + int r, g, b, a, d, s, sample_buffers, samples; + IntPtr active_config = configs[0]; + Egl.GetConfigAttrib(egl_display, active_config, Egl.RED_SIZE, out r); + Egl.GetConfigAttrib(egl_display, active_config, Egl.GREEN_SIZE, out g); + Egl.GetConfigAttrib(egl_display, active_config, Egl.BLUE_SIZE, out b); + Egl.GetConfigAttrib(egl_display, active_config, Egl.ALPHA_SIZE, out a); + Egl.GetConfigAttrib(egl_display, active_config, Egl.DEPTH_SIZE, out d); + Egl.GetConfigAttrib(egl_display, active_config, Egl.STENCIL_SIZE, out s); + Egl.GetConfigAttrib(egl_display, active_config, Egl.SAMPLE_BUFFERS, out sample_buffers); + Egl.GetConfigAttrib(egl_display, active_config, Egl.SAMPLES, out samples); + Console.WriteLine ("EGL context: {0},{1},{2},{3} depth={4} stencil={5} samples={6} sample buffers={7}", + r, g, b, a, d, s, samples, sample_buffers); + + + if (!Egl.MakeCurrent(egl_display, egl_surface, egl_surface, egl_ctx)) + throw new NotSupportedException(string.Format("Failed to make context {0} current. Error: {1}", gbm_surface, Egl.GetError())); + } + + void initCairo (){ + cairoDev = new Cairo.EGLDevice (egl_display, egl_ctx); + + cairoSurf = new Cairo.GLSurface (cairoDev, egl_surface, width, height); + //cairoSurf = new Cairo.EGLSurface (cairoDev, egl_surface, 1600, 900); + + cairoDev.SetThreadAware (false); + + if (cairoDev.Acquire () != Cairo.Status.Success) + Console.WriteLine ("[Cairo]: Failed to acquire egl device."); + } + #endregion + + #region cursor + BufferObject boMouseCursor = BufferObject.Zero; + + internal void updateCursor (XCursor cursor) { + uint width = 64, height = 64; + if (cursor.Width > width || cursor.Height > height){ + Debug.Print("[DRM] Cursor size {0}x{1} unsupported. Maximum is 64x64.", + cursor.Width, cursor.Height); + return; + } + + if (boMouseCursor != BufferObject.Zero) + Gbm.DestroyBuffer (boMouseCursor); + + boMouseCursor = Gbm.CreateBuffer (gbm_device, (int)width, (int)height, + SurfaceFormat.ARGB8888, SurfaceFlags.Cursor64x64 | SurfaceFlags.Write); + + if (boMouseCursor == BufferObject.Zero) + { + Debug.Print("[DRM] Failed to create buffer for mouse cursor."); + return; + } + + // Copy cursor.Data into a new buffer of the correct size + byte[] cursor_data = new byte[width * height * 4]; + for (uint y = 0; y < cursor.Height; y++) + { + uint dst_offset = y * width * 4; + uint src_offset = y * cursor.Width * 4; + uint src_length = cursor.Width * 4; + Array.Copy( + cursor.data, src_offset, + cursor_data, dst_offset, + src_length); + } + + boMouseCursor.Write(cursor_data); + + unsafe { + Drm.SetCursor (fd_gpu, pEncoder->crtc_id, + boMouseCursor.Handle, boMouseCursor.Width, boMouseCursor.Height, (int)cursor.Xhot, (int)cursor.Yhot); + } + } + #endregion + + int getFbFromBo (BufferObject bo){ + int width = bo.Width; + int height = bo.Height; + int bpp = 32; + int depth = 24; + int stride = bo.Stride; + int hndBO = bo.Handle; + + int fb; + int ret = Drm.ModeAddFB (fd_gpu, width, height,(byte)depth, (byte)bpp, stride, hndBO, out fb); + if (ret != 0) + throw new Exception ("[DRM]: ModeAddFB failed."); + bo.SetUserData ((IntPtr)fb, DestroyFBPtr); + return fb; + } + + public void Run(){ + BufferObject bo; + int fb; + + PollFD fds = new PollFD(); + fds.fd = fd_gpu; + fds.events = PollFlags.In; + + EventContext evctx = new EventContext(); + evctx.version = EventContext.Version; + evctx.page_flip_handler = PageFlipPtr; + + int timeout = -1;//block ? -1 : 0; + + using (Cairo.Context ctx = new Cairo.Context (cairoSurf)) { + ctx.Rectangle (0, 0, width, height); + ctx.SetSourceRGB (0, 0, 0); + ctx.Fill (); + } + + cairoSurf.SwapBuffers (); + + bo = Gbm.LockFrontBuffer (gbm_surface); + fb = getFbFromBo (bo); + + SetScanoutRegion (fb); + drmTimeOut.Restart(); + + while (run && drmTimeOut.ElapsedMilliseconds < 10000){ + BufferObject next_bo; + bool update = false; + + if (updateMousePos) { + lock (Sync) { + updateMousePos = false; + unsafe { + Drm.MoveCursor (fd_gpu, pEncoder->crtc_id, MouseX-8, MouseY-4); + } + } + } + + if (Monitor.TryEnter (CrowInterface.RenderMutex)) { + if (CrowInterface.IsDirty) { + CrowInterface.IsDirty = false; + update = true; + using (Cairo.Context ctx = new Cairo.Context (cairoSurf)) { + using (Cairo.Surface d = new Cairo.ImageSurface (CrowInterface.dirtyBmp, Cairo.Format.Argb32, + width, height, width * 4)) { + ctx.SetSourceSurface (d, 0, 0); + ctx.Operator = Cairo.Operator.Source; + ctx.Paint (); + } + } + } + Monitor.Exit (CrowInterface.RenderMutex); + } + + if (!update) + continue; + update = false; + + cairoSurf.Flush (); + cairoSurf.SwapBuffers (); + + if (Gbm.HasFreeBuffers (gbm_surface) == 0) + throw new Exception ("[GBM]: Out of free buffers."); + + next_bo = Gbm.LockFrontBuffer (gbm_surface); + if (next_bo == BufferObject.Zero) + throw new Exception ("[GBM]: Failed to lock front buffer."); + + fb = getFbFromBo (next_bo); + + unsafe{ + int is_flip_queued = 1; + + while (Drm.ModePageFlip (fd_gpu, pEncoder->crtc_id, fb, PageFlipFlags.FlipEvent, ref is_flip_queued) < 0) { + //Console.WriteLine ("[DRM] Failed to enqueue framebuffer flip."); + continue; + } + + while (is_flip_queued != 0) + { + fds.revents = 0; + if (Libc.poll (ref fds, 1, timeout) < 0) + break; + + if ((fds.revents & (PollFlags.Hup | PollFlags.Error)) != 0) + break; + + if ((fds.revents & PollFlags.In) != 0) + Drm.HandleEvent (fd_gpu, ref evctx); + else + break; + } + if (is_flip_queued != 0) + Console.WriteLine ("flip canceled"); + + Gbm.ReleaseBuffer (gbm_surface, bo); + //Drm.ModeRmFB(fd_gpu, fb); + + bo = next_bo; + next_bo = BufferObject.Zero; + + } + } + } + + #region rendering + + + + // We only support a SwapInterval of 0 (immediate) + // or 1 (vsynced). + // Todo: add support for SwapInterval of -1 (adaptive). + // This requires a small change in WaitFlip(). + int swap_interval=0; + + readonly IntPtr PageFlipPtr; + readonly PageFlipCallback PageFlip; + + readonly IntPtr DestroyFBPtr; + readonly DestroyUserDataCallback DestroyFB; + + void HandlePageFlip(int fd, int sequence, int tv_sec, int tv_usec, ref int user_data) + { + user_data = 0; + } + + + void HandleDestroyFB(BufferObject bo, IntPtr data) + { + Console.WriteLine ("DestroyFB"); + IntPtr gbm = bo.Device; + int fb = data.ToInt32(); + + if (fb != 0) + Drm.ModeRmFB(fd_gpu, fb); + } + + void SetScanoutRegion(int buffer) + { + unsafe + { + ModeInfo* mode = pConnector->modes; + int connector_id = pConnector->connector_id; + int crtc_id = pEncoder->crtc_id; + + int ret = Drm.ModeSetCrtc (fd_gpu, crtc_id, buffer, 0, 0, &connector_id, 1, mode); + + if (ret != 0) + Debug.Print("[KMS] Drm.ModeSetCrtc{0}, {1}, {2} failed. Error: {3}", + fd_gpu, crtc_id, buffer, ret); + Console.WriteLine ("scanout region set: {0}x{1}", mode->hdisplay, mode->vdisplay); + } + } + #endregion + + #region IDisposable implementation + public void Dispose () + { + cairoDev.Release (); + cairoSurf.Dispose (); + cairoDev.Dispose (); + + // if (fb != 0) + // Drm.ModeRmFB (fd_gpu, fb); + // if (bo != BufferObject.Zero) + // Gbm.ReleaseBuffer (gbm_surface, bo); + // if (next_fb != 0) + // Drm.ModeRmFB (fd_gpu, next_fb); + // if (next_bo != BufferObject.Zero) + // Gbm.ReleaseBuffer (gbm_surface, next_bo); + + if (Egl.GetCurrentContext () == egl_ctx) { + Console.WriteLine ("destroying context"); + Egl.DestroyContext (egl_display, egl_ctx); + }else + Console.WriteLine ("not current"); + + cairoDev.Dispose (); + + unsafe{ + Drm.ModeSetCrtc (fd_gpu, saved_crtc->crtc_id, saved_crtc->buffer_id, + saved_crtc->x, saved_crtc->y, &pConnector->connector_id, 1, &saved_crtc->mode); + Drm.ModeFreeCrtc ((IntPtr)saved_crtc); + } + + Drm.ModeFreeCrtc (Crtc); + Drm.ModeFreeConnector(Connector); + Drm.ModeFreeEncoder(Encoder); + Libc.close(fd_gpu); + +// using(VTControler master = new VTControler()){ +// master.CurrentMode = TTY.Mode.TEXT; +// if (master.CurrentVT != previousVT) { +// master.SwitchTo (previousVT); +// } +// } + } + #endregion + + #region INPUT + Thread input_thread; + long exit; + + static readonly object Sync = new object(); + static readonly Crow.Key[] KeyMap = Evdev.KeyMap; + static long DeviceFDCount; + + IntPtr udev; + IntPtr input_context; + + int input_fd = 0; + + InputInterface input_interface = new InputInterface( + OpenRestricted, CloseRestricted); + static CloseRestrictedCallback CloseRestricted = CloseRestrictedHandler; + static void CloseRestrictedHandler(int fd, IntPtr data) + { + Debug.Print("[Input] Closing fd {0}", fd); + int ret = Libc.close(fd); + + if (ret < 0) + { + Debug.Print("[Input] Failed to close fd {0}. Error: {1}", fd, ret); + } + else + { + Interlocked.Decrement(ref DeviceFDCount); + } + } + + static OpenRestrictedCallback OpenRestricted = OpenRestrictedHandler; + static int OpenRestrictedHandler(IntPtr path, int flags, IntPtr data) + { + int fd = Libc.open(path, (OpenFlags)flags); + Debug.Print("[Input] Opening '{0}' with flags {1}. fd:{2}", + Marshal.PtrToStringAnsi(path), (OpenFlags)flags, fd); + + if (fd >= 0) + { + Interlocked.Increment(ref DeviceFDCount); + } + + return fd; + } + + void initInput (){ + Semaphore ready = new Semaphore(0, 1); + input_thread = new Thread (InputThreadLoop); + input_thread.IsBackground = true; + input_thread.Start(ready); + } + + void InputThreadLoop(object semaphore) + { + Debug.Print("[Input] Running on thread {0}", Thread.CurrentThread.ManagedThreadId); + Setup(); + + // Inform the parent thread that initialization has completed successfully + (semaphore as Semaphore).Release(); + Debug.Print("[Input] Released main thread.", input_context); + + // Use a blocking poll for input messages, in order to reduce CPU usage + PollFD poll_fd = new PollFD(); + poll_fd.fd = input_fd; + poll_fd.events = PollFlags.In; + Debug.Print("[Input] Created PollFD({0}, {1})", poll_fd.fd, poll_fd.events); + + Debug.Print("[Input] Entering input loop.", poll_fd.fd, poll_fd.events); + while (Interlocked.Read(ref exit) == 0) + { + drmTimeOut.Restart (); + + int ret = Libc.poll(ref poll_fd, 1, -1); + ErrorNumber error = (ErrorNumber)Marshal.GetLastWin32Error(); + bool is_error = + ret < 0 && !(error == ErrorNumber.Again || error == ErrorNumber.Interrupted) || + (poll_fd.revents & (PollFlags.Hup | PollFlags.Error | PollFlags.Invalid)) != 0; + + if (ret > 0 && (poll_fd.revents & (PollFlags.In | PollFlags.Pri)) != 0) + ProcessEvents(input_context); + + if (is_error) + { + Debug.Print("[Input] Exiting input loop {0} due to poll error [ret:{1} events:{2}]. Error: {3}.", + input_thread.ManagedThreadId, ret, poll_fd.revents, error); + Interlocked.Increment(ref exit); + } + } + Debug.Print("[Input] Exited input loop.", poll_fd.fd, poll_fd.events); + } + + void Setup() + { + // Todo: add static path fallback when udev is not installed. + udev = Udev.New(); + if (udev == IntPtr.Zero) + { + Debug.Print("[Input] Udev.New() failed."); + Interlocked.Increment(ref exit); + return; + } + Debug.Print("[Input] Udev.New() = {0:x}", udev); + + input_context = LibInput.CreateContext(input_interface, IntPtr.Zero, udev); + if (input_context == IntPtr.Zero) + { + Debug.Print("[Input] LibInput.CreateContext({0:x}) failed.", udev); + Interlocked.Increment(ref exit); + return; + } + Debug.Print("[Input] LibInput.CreateContext({0:x}) = {1:x}", udev, input_context); + + string seat_id = "seat0"; + int seat_assignment = LibInput.AssignSeat(input_context, seat_id); + if (seat_assignment == -1) + { + Debug.Print("[Input] LibInput.AssignSeat({0:x}) = {1} failed.", input_context, seat_id); + Interlocked.Increment(ref exit); + return; + } + Debug.Print("[Input] LibInput.AssignSeat({0:x}) = {1}", input_context, seat_id); + + input_fd = LibInput.GetFD(input_context); + if (input_fd < 0) + { + Debug.Print("[Input] LibInput.GetFD({0:x}) failed.", input_context); + Interlocked.Increment(ref exit); + return; + } + Debug.Print("[Input] LibInput.GetFD({0:x}) = {1}.", input_context, input_fd); + + ProcessEvents(input_context); + LibInput.Resume(input_context); + Debug.Print("[Input] LibInput.Resume({0:x})", input_context); + + if (Interlocked.Read(ref DeviceFDCount) <= 0) + { + Debug.Print("[Error] Failed to open any input devices."); + Debug.Print("[Error] Ensure that you have access to '/dev/input/event*'."); + Interlocked.Increment(ref exit); + } + } + + void ProcessEvents(IntPtr input_context) + { + // Process all events in the event queue + while (true) + { + // Data available + int ret = LibInput.Dispatch(input_context); + if (ret != 0) + { + Debug.Print("[Input] LibInput.Dispatch({0:x}) failed. Error: {1}", + input_context, ret); + break; + } + + IntPtr pevent = LibInput.GetEvent(input_context); + if (pevent == IntPtr.Zero) + { + break; + } + + IntPtr device = LibInput.GetDevice(pevent); + InputEventType type = LibInput.GetEventType(pevent); + + lock (Sync) + { + switch (type) + { + // case InputEventType.DeviceAdded: + // HandleDeviceAdded(input_context, device); + // break; + // + // case InputEventType.DeviceRemoved: + // HandleDeviceRemoved(input_context, device); + // break; + // + case InputEventType.KeyboardKey: + //run = false; + handleKeyboard(LibInput.GetKeyboardEvent(pevent)); + break; + // + // case InputEventType.PointerAxis: + // HandlePointerAxis(GetMouse(device), LibInput.GetPointerEvent(pevent)); + // break; + // + case InputEventType.PointerButton: + handlePointerButton (LibInput.GetPointerEvent(pevent)); + break; + + case InputEventType.PointerMotion: + handlePointerMotion (LibInput.GetPointerEvent(pevent)); + break; + + // case InputEventType.PointerMotionAbsolute: + // HandlePointerMotionAbsolute(GetMouse(device), LibInput.GetPointerEvent(pevent)); + // break; + } + } + + LibInput.DestroyEvent(pevent); + } + } + int MouseX = 0, MouseY = 0; + volatile bool updateMousePos = true; + + int roundDelta (double d){ + return d > 0 ? (int)Math.Ceiling(d) : (int)Math.Floor (d); + } + + void handlePointerMotion(PointerEvent e) + { + MouseX += roundDelta (e.DeltaX); + MouseY += roundDelta (e.DeltaY); + + Rectangle bounds = CrowInterface.ClientRectangle; + if (MouseX < bounds.Left) + MouseX = bounds.Left; + else if (MouseX > bounds.Right) + MouseX = bounds.Right; + + if (MouseY < bounds.Top) + MouseY = bounds.Top; + else if (MouseY > bounds.Bottom) + MouseY = bounds.Bottom; + + CrowInterface.ProcessMouseMove (MouseX, MouseY); + + updateMousePos = true; + } + void handlePointerButton (PointerEvent e) + { + int but = 0; + switch (e.Button) { + case EvdevButton.LEFT: + but = 0; + break; + case EvdevButton.MIDDLE: + but = 1; + break; + case EvdevButton.RIGHT: + but = 2; + break; + } + if (e.ButtonState == ButtonState.Pressed) + CrowInterface.ProcessMouseButtonDown (but); + else + CrowInterface.ProcessMouseButtonUp (but); + } + + KeyModifiers curModifiers = KeyModifiers.None; + + void handleKeyboard(KeyboardEvent e) + { + return; + int key = (int)Evdev.KeyMap [e.Key]; + Key k = (Key)key; + if (e.KeyState == KeyState.Pressed) { + CrowInterface.ProcessKeyDown (key); + switch (k) { + case Key.ShiftLeft: + case Key.ShiftRight: + curModifiers |= KeyModifiers.Shift; + break; + case Key.ControlLeft: + case Key.ControlRight: + curModifiers |= KeyModifiers.Control; + break; + case Key.AltLeft: + curModifiers |= KeyModifiers.Alt; + break; + case Key.AltRight: + curModifiers |= KeyModifiers.AltGr; + break; + } + }else { + CrowInterface.ProcessKeyUp (key); + switch (k) { + case Key.ShiftLeft: + case Key.ShiftRight: + curModifiers &= ~KeyModifiers.Shift; + break; + case Key.ControlLeft: + case Key.ControlRight: + curModifiers &= ~KeyModifiers.Control; + break; + case Key.AltLeft: + curModifiers &= ~KeyModifiers.Alt; + break; + case Key.AltRight: + curModifiers &= ~KeyModifiers.AltGr; + break; + } + if (!keymap.ContainsKey (curModifiers)) { + Console.WriteLine ("keymap not found for: " + curModifiers + " " + (int)curModifiers); + return; + } +// string tmp = keymap [curModifiers] [e.Key]; +// if (string.IsNullOrEmpty (tmp)) +// return; +// if (char.IsControl (tmp[0])) +// return; +// CrowInterface.ProcessKeyPress (tmp [0]); + } + } + + #endregion + } +} + + + + diff --git a/testDrm/src/Egl.cs b/testDrm/src/Linux/Bindings/Egl.cs similarity index 99% rename from testDrm/src/Egl.cs rename to testDrm/src/Linux/Bindings/Egl.cs index 434a54e7..f9919265 100644 --- a/testDrm/src/Egl.cs +++ b/testDrm/src/Linux/Bindings/Egl.cs @@ -31,7 +31,7 @@ using System.Collections.Generic; using System.Text; -namespace OpenTK.Platform.Egl +namespace Crow.Linux { using EGLNativeDisplayType = IntPtr; using EGLNativeWindowType = IntPtr; diff --git a/testDrm/src/Linux/Bindings/Evdev.cs b/testDrm/src/Linux/Bindings/Evdev.cs index fa4f725f..d162c071 100644 --- a/testDrm/src/Linux/Bindings/Evdev.cs +++ b/testDrm/src/Linux/Bindings/Evdev.cs @@ -25,6 +25,9 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // +using OpenTK; + + #endregion using System; @@ -32,7 +35,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using Crow; -namespace OpenTK.Platform.Linux +namespace Crow.Linux { // Bindings for linux/input.h class Evdev diff --git a/testDrm/src/Linux/Bindings/LibInput.cs b/testDrm/src/Linux/Bindings/LibInput.cs index d45f55de..b7c05f6e 100644 --- a/testDrm/src/Linux/Bindings/LibInput.cs +++ b/testDrm/src/Linux/Bindings/LibInput.cs @@ -33,7 +33,7 @@ using System; using System.Diagnostics; using System.Runtime.InteropServices; -namespace OpenTK.Platform.Linux +namespace Crow.Linux { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate int OpenRestrictedCallback(IntPtr path, int flags, IntPtr data); diff --git a/testDrm/src/Linux/Bindings/Libc.cs b/testDrm/src/Linux/Bindings/Libc.cs index 11318756..c39330c2 100644 --- a/testDrm/src/Linux/Bindings/Libc.cs +++ b/testDrm/src/Linux/Bindings/Libc.cs @@ -33,7 +33,7 @@ using System.Text; #pragma warning disable 0649 // field is never assigned -namespace OpenTK.Platform.Linux +namespace Crow.Linux { partial class Libc { @@ -69,11 +69,24 @@ namespace OpenTK.Platform.Linux [DllImport(lib)] public static extern int open(IntPtr pathname, OpenFlags flags); + [DllImport(lib)] + public static extern int posix_openpt (OpenFlags flags); + [DllImport(lib)] public static extern int close(int fd); [DllImport(lib)] unsafe public static extern IntPtr read(int fd, void* buffer, UIntPtr count); + [DllImport(lib)] + unsafe public static extern uint write(int fd, void *buffer, int count); + + public static void write (int fd, byte[] b){ + unsafe { + fixed (byte* pb = b) { + write (fd, pb, b.Length); + } + } + } public static int read(int fd, out byte b) { diff --git a/testDrm/src/Linux/Bindings/Poll.cs b/testDrm/src/Linux/Bindings/Poll.cs index f78d6d73..cdd9c412 100644 --- a/testDrm/src/Linux/Bindings/Poll.cs +++ b/testDrm/src/Linux/Bindings/Poll.cs @@ -30,7 +30,7 @@ using System; using System.Runtime.InteropServices; -namespace OpenTK.Platform.Linux +namespace Crow.Linux { partial class Libc { diff --git a/testDrm/src/Linux/DRM.cs b/testDrm/src/Linux/DRM.cs new file mode 100644 index 00000000..3bdd9504 --- /dev/null +++ b/testDrm/src/Linux/DRM.cs @@ -0,0 +1,291 @@ +// +// DRM.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Runtime.InteropServices; + +namespace Crow.Linux.DRI { + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void VBlankCallback(int fd, int sequence, int tv_sec, int tv_usec, IntPtr user_data); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void PageFlipCallback(int fd, int sequence, int tv_sec, int tv_usec, ref int user_data); + + enum ModeConnection + { + Connected = 1, + Disconnected = 2, + Unknown = 3 + } + enum ModeConnectorType + { + Unknown = 0, + VGA=1, + DVII=2, + DVID=3, + DVIA=4, + Composite=5, + SVIDEO=6, + LVDS=7, + Component=8, + PinDIN9 = 9, + DisplayPort=10, + HDMIA=11, + HDMIB=12, + TV=13, + eDP=14, + VIRTUAL=15, + DSI=16, + DPI=17 + } + enum ModeEncoderType + { + NONE=0, + DAC=1, + TMDS=2, + LVDS=3, + TVDAC=4, + VIRTUAL=5, + DSI=6, + DPMST=7, + DPI=8, + } + enum ModeSubPixel + { + Unknown = 1, + HorizontalRgb = 2, + HorizontalBgr = 3, + VerticalRgb = 4, + VerticalBgr = 5, + None = 6 + } + + [Flags] + enum PageFlipFlags + { + FlipEvent = 0x01, + FlipAsync = 0x02, + FlipFlags = FlipEvent | FlipAsync + } + + [Flags] + enum ModeFlags + { + /* Video mode flags */ + /* bit compatible with the xorg definitions. */ + PHSYNC = 0x01, + NHSYNC = 0x02, + PVSYNC = 0x04, + NVSYNC = 0x08, + INTERLACE = 0x10, + DBLSCAN = 0x20, + CSYNC = 0x40, + PCSYNC = 0x80, + NCSYNC = 0x10, + HSKEW = 0x0200, + BCAST = 0x0400, + PIXMUX = 0x0800, + DBLCLK = 0x1000, + CLKDIV2 = 0x2000, + // FLAG_3D_MASK (0x1f<<14) + // FLAG_3D_NONE = 0x0; + // FLAG_3D_FRAME_PACKING = 0x4000, + // FLAG_3D_FIELD_ALTERNATIVE = 0x8000, + // FLAG_3D_LINE_ALTERNATIVE (3<<14) + // FLAG_3D_SIDE_BY_SIDE_FULL (4<<14) + // FLAG_3D_L_DEPTH (5<<14) + // FLAG_3D_L_DEPTH_GFX_GFX_DEPTH (6<<14) + // FLAG_3D_TOP_AND_BOTTOM (7<<14) + // FLAG_3D_SIDE_BY_SIDE_HALF (8<<14) + } + + [StructLayout(LayoutKind.Sequential)] + public struct EventContext + { + public int version; + public IntPtr vblank_handler; + public IntPtr page_flip_handler; + public static readonly int Version = 2; + } + + [StructLayout(LayoutKind.Sequential)] + unsafe struct ModeConnector + { + public int connector_id; + public int encoder_id; + public ModeConnectorType connector_type; + public int connector_type_id; + public ModeConnection connection; + public int mmWidth, mmHeight; + public ModeSubPixel subpixel; + + public int count_modes; + public ModeInfo* modes; + + public int count_props; + public int *props; + public long *prop_values; + + public int count_encoders; + public int *encoders; + } + + [StructLayout(LayoutKind.Sequential)] + struct ModeCrtc + { + public int crtc_id; + public int buffer_id; + + public int x, y; + public int width, height; + public int mode_valid; + public ModeInfo mode; + + public int gamma_size; + } + + [StructLayout(LayoutKind.Sequential)] + struct ModeEncoder + { + public int encoder_id; + public ModeEncoderType encoder_type; + public int crtc_id; + public int possible_crtcs; + public int possible_clones; + } + + [StructLayout(LayoutKind.Sequential)] + unsafe struct ModeInfo + { + public uint clock; + public ushort hdisplay, hsync_start, hsync_end, htotal, hskew; + public ushort vdisplay, vsync_start, vsync_end, vtotal, vscan; + + public int vrefresh; // refresh rate * 1000 + + public uint flags; + public uint type; + public fixed sbyte name[32]; + } + + [StructLayout(LayoutKind.Sequential)] + unsafe struct ModeRes + { + public int count_fbs; + public uint* fbs; + public int count_crtcs; + public uint* crtcs; + public int count_connectors; + public uint* connectors; + public int count_encoders; + public uint* encoders; + public uint min_width, max_width; + public uint min_height, max_height; + } + + + + public class GPUControler : IDisposable { + int fd_gpu = -1; + ModeRes resources = new ModeRes (); + + public GPUControler(string gpu_path = "/dev/dri/card0"){ + fd_gpu = Libc.open(gpu_path, OpenFlags.ReadWrite | OpenFlags.CloseOnExec); + if (fd_gpu < 0) + throw new NotSupportedException("[KMS] Failed to open gpu"); + + unsafe { + ModeRes* ptrRes = ModeGetResources (fd_gpu); + resources = *ptrRes; + ModeFreeResources (ptrRes); + } + +// if (resources == null) +// throw new NotSupportedException("[KMS] Drm.ModeGetResources failed."); + } + + #region IDisposable implementation + ~GPUControler(){ + Dispose (false); + } + public void Dispose () + { + Dispose (true); + GC.SuppressFinalize (this); + } + protected virtual void Dispose (bool disposing){ + if (fd_gpu > 0) + Libc.close (fd_gpu); + fd_gpu = -1; + } + #endregion + + #region ioctl overrides + const string lib = "libdrm"; + [DllImport(lib, EntryPoint = "drmHandleEvent", CallingConvention = CallingConvention.Cdecl)] + public static extern int HandleEvent(int fd, ref EventContext evctx); + + [DllImport(lib, EntryPoint = "drmModeAddFB", CallingConvention = CallingConvention.Cdecl)] + public static extern int ModeAddFB(int fd, int width, int height, byte depth, + byte bpp, int pitch, int bo_handle, + out int buf_id); + + [DllImport(lib, EntryPoint = "drmModeRmFB", CallingConvention = CallingConvention.Cdecl)] + public static extern int ModeRmFB(int fd, int bufferId); + + + [DllImport(lib, EntryPoint = "drmModeGetResources", CallingConvention = CallingConvention.Cdecl)] + unsafe static extern ModeRes* ModeGetResources(int fd); + [DllImport(lib, EntryPoint = "drmModeGetCrtc", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr ModeGetCrtc(int fd, int crtcId); + [DllImport(lib, EntryPoint = "drmModeGetConnector", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr ModeGetConnector(int fd, int connector_id); + [DllImport(lib, EntryPoint = "drmModeGetEncoder", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr ModeGetEncoder(int fd, int encoder_id); + + [DllImport(lib, EntryPoint = "drmModeFreeResources", CallingConvention = CallingConvention.Cdecl)] + unsafe static extern void ModeFreeResources(ModeRes* ptr); + [DllImport(lib, EntryPoint = "drmModeFreeCrtc", CallingConvention = CallingConvention.Cdecl)] + public static extern void ModeFreeCrtc(IntPtr ptr); + [DllImport(lib, EntryPoint = "drmModeFreeConnector", CallingConvention = CallingConvention.Cdecl)] + public static extern void ModeFreeConnector(IntPtr ptr); + [DllImport(lib, EntryPoint = "drmModeFreeEncoder", CallingConvention = CallingConvention.Cdecl)] + public static extern void ModeFreeEncoder(IntPtr ptr); + + [DllImport(lib, EntryPoint = "drmModePageFlip", CallingConvention = CallingConvention.Cdecl)] + static extern int ModePageFlip(int fd, int crtc_id, int fb_id, + PageFlipFlags flags, ref int user_data); + + [DllImport(lib, EntryPoint = "drmModeSetCrtc", CallingConvention = CallingConvention.Cdecl)] + unsafe static extern int ModeSetCrtc(int fd, int crtcId, int bufferId, + int x, int y, int* connectors, int count, ModeInfo* mode); + + [DllImport(lib, EntryPoint = "drmModeSetCursor2", CallingConvention = CallingConvention.Cdecl)] + public static extern int SetCursor(int fd, int crtcId, int bo_handle, int width, int height, int hot_x, int hot_y); + + [DllImport(lib, EntryPoint = "drmModeMoveCursor", CallingConvention = CallingConvention.Cdecl)] + public static extern int MoveCursor(int fd, int crtcId, int x, int y); + #endregion + } +} \ No newline at end of file diff --git a/testDrm/src/Linux/LinuxDisplayDriver.cs b/testDrm/src/Linux/LinuxDisplayDriver.cs deleted file mode 100644 index 74aed632..00000000 --- a/testDrm/src/Linux/LinuxDisplayDriver.cs +++ /dev/null @@ -1,415 +0,0 @@ -#region License -// -// LinuxDisplayDriver.cs -// -// Author: -// Stefanos A. -// -// Copyright (c) 2006-2014 Stefanos Apostolopoulos -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -#endregion - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using OpenTK; -#if !MINIMAL -using System.Drawing; -#endif - -namespace OpenTK.Platform.Linux -{ - // Stores platform-specific information about a display - class LinuxDisplay - { - public int FD; - public IntPtr Connector; - public IntPtr Crtc; - public IntPtr Encoder; - - unsafe public ModeConnector* pConnector { get { return (ModeConnector*)Connector; } } - unsafe public ModeCrtc* pCrtc { get { return (ModeCrtc*)Crtc; } } - unsafe public ModeEncoder* pEncoder { get { return (ModeEncoder*)Encoder; } } - /* - public ModeInfo Mode - { - get - { - if (Crtc == IntPtr.Zero) - throw new InvalidOperationException(); - - unsafe - { - return pCrtc->mode; - } - } - } - */ - - public ModeInfo OriginalMode; - - public int Id - { - get - { - if (Crtc == IntPtr.Zero) - throw new InvalidOperationException(); - - unsafe - { - return (int)pCrtc->crtc_id; - } - } - } - - public LinuxDisplay(int fd, IntPtr c, IntPtr e, IntPtr r) - { - FD = fd; - Connector = c; - Encoder = e; - Crtc = r; - unsafe - { - OriginalMode = pCrtc->mode; // in case we change resolution later on - } - } - } - - class LinuxDisplayDriver - { - readonly int FD; - readonly Dictionary DisplayIds = - new Dictionary(); - - public LinuxDisplayDriver(int fd) - { - Debug.Print("[KMS] Creating LinuxDisplayDriver for fd:{0}", fd); - Debug.Indent(); - try - { - FD = fd; - UpdateDisplays(fd); - } - finally - { - Debug.Unindent(); - } - } - - /// \internal - /// - /// Queries the specified GPU for connected displays and, optionally, - /// returns the list of displays. - /// - /// true, if at least one display is connected, false otherwise. - /// The fd for the GPU to query, obtained through open("/dev/dri/card0"). - /// - /// If not null, this will contain a list instances, - /// one for each connected display. - /// - internal static bool QueryDisplays(int fd, List displays) - { - unsafe - { - bool has_displays = false; - if (displays != null) - { - displays.Clear(); - } - - ModeRes* resources = (ModeRes*)Drm.ModeGetResources(fd); - if (resources == null) - { - Debug.Print("[KMS] Drm.ModeGetResources failed."); - return false; - } - Debug.Print("[KMS] DRM found {0} connectors", resources->count_connectors); - - // Search for a valid connector - ModeConnector* connector = null; - for (int i = 0; i < resources->count_connectors; i++) - { - connector = (ModeConnector*)Drm.ModeGetConnector(fd, - *(resources->connectors + i)); - if (connector != null) - { - bool success = false; - LinuxDisplay display = null; - try - { - if (connector->connection == ModeConnection.Connected && - connector->count_modes > 0) - { - success = QueryDisplay(fd, connector, out display); - has_displays |= success; - } - } - catch (Exception e) - { - Debug.Print("[KMS] Failed to add display. Error: {0}", e); - } - - if (success && displays != null) - { - displays.Add(display); - } - else - { - Drm.ModeFreeConnector((IntPtr)connector); - connector = null; - } - } - } - - return has_displays; - } - } - - void UpdateDisplays(int fd) - { - unsafe - { - lock (this) - { - AvailableDevices.Clear(); - DisplayIds.Clear(); - - List displays = new List(); - if (QueryDisplays(fd, displays)) - { - foreach (LinuxDisplay display in displays) - { - AddDisplay(display); - } - } - - if (AvailableDevices.Count == 0) - { - Debug.Print("[KMS] Failed to find any active displays"); - } - } - } - } - - unsafe static ModeEncoder* GetEncoder(int fd, ModeConnector* c) - { - ModeEncoder* encoder = null; - for (int i = 0; i < c->count_encoders && encoder == null; i++) - { - ModeEncoder* e = (ModeEncoder*)Drm.ModeGetEncoder( - fd, *(c->encoders + i)); - if (e != null) - { - if (e->encoder_id == c->encoder_id) - { - encoder = e; - } - else - { - Drm.ModeFreeEncoder((IntPtr)e); - } - } - } - - if (encoder != null) - { - Debug.Print("[KMS] Encoder {0} found for connector {1}", - encoder->encoder_id, c->connector_id); - } - else - { - Debug.Print("[KMS] Failed to find encoder for connector {0}", c->connector_id); - } - - return encoder; - } - - unsafe static ModeCrtc* GetCrtc(int fd, ModeEncoder* encoder) - { - ModeCrtc* crtc = (ModeCrtc*)Drm.ModeGetCrtc(fd, encoder->crtc_id); - if (crtc != null) - { - Debug.Print("[KMS] CRTC {0} found for encoder {1}", - encoder->crtc_id, encoder->encoder_id); - } - else - { - Debug.Print("[KMS] Failed to find crtc {0} for encoder {1}", - encoder->crtc_id, encoder->encoder_id); - } - return crtc; - } - - unsafe static void GetModes(LinuxDisplay display, DisplayResolution[] modes, out DisplayResolution current) - { - int mode_count = display.pConnector->count_modes; - Debug.Print("[KMS] Display supports {0} mode(s)", mode_count); - for (int i = 0; i < mode_count; i++) - { - ModeInfo* mode = display.pConnector->modes + i; - if (mode != null) - { - Debug.Print("Mode {0}: {1}x{2} @{3}", i, - mode->hdisplay, mode->vdisplay, mode->vrefresh); - DisplayResolution res = GetDisplayResolution(mode); - modes[i] = res; - } - } - - if (display.pCrtc->mode_valid != 0) - { - ModeInfo cmode = display.pCrtc->mode; - current = GetDisplayResolution(&cmode); - } - else - { - current = GetDisplayResolution(display.pConnector->modes); - } - Debug.Print("Current mode: {0}", current.ToString()); - } - - Rectangle GetBounds(DisplayResolution current) - { - // Note: since we are not running a display manager, we are free - // to choose the display layout for multiple displays ourselves. - // We choose the simplest layout: displays are laid out side-by-side - // from left to right. Primary display is the first display we encounter. - int x = AvailableDevices.Count == 0 ? - 0 : AvailableDevices[AvailableDevices.Count - 1].Bounds.Right; - int y = 0; - - return new Rectangle( - x, y, current.Width, current.Height); - } - - void UpdateDisplayIndices(LinuxDisplay display, DisplayDevice device) - { - if (!DisplayIds.ContainsKey(display.Id)) - { - Debug.Print("[KMS] Adding display {0} as {1}", display.Id, AvailableDevices.Count); - DisplayIds.Add(display.Id, AvailableDevices.Count); - } - int index = DisplayIds[display.Id]; - if (index >= AvailableDevices.Count) - { - AvailableDevices.Add(device); - } - else - { - AvailableDevices[index] = device; - } - } - - unsafe static bool QueryDisplay(int fd, ModeConnector* c, out LinuxDisplay display) - { - display = null; - - // Find corresponding encoder - ModeEncoder* encoder = GetEncoder(fd, c); - if (encoder == null) - return false; - - ModeCrtc* crtc = GetCrtc(fd, encoder); - if (crtc == null) - return false; - - display = new LinuxDisplay(fd, (IntPtr)c, (IntPtr)encoder, (IntPtr)crtc); - return true; - } - - unsafe void AddDisplay(LinuxDisplay display) - { - DisplayResolution[] modes = new DisplayResolution[display.pConnector->count_modes]; - DisplayResolution current; - GetModes(display, modes, out current); - - bool is_primary = AvailableDevices.Count == 0; - DisplayDevice device = new DisplayDevice(current, is_primary, - modes, GetBounds(current), display); - - if (is_primary) - { - Primary = device; - } - - UpdateDisplayIndices(display, device); - - Debug.Print("[KMS] Added DisplayDevice {0}", device); - } - - unsafe static DisplayResolution GetDisplayResolution(ModeInfo* mode) - { - return new DisplayResolution( - 0, 0, - mode->hdisplay, mode->vdisplay, - 32, // This is actually part of the framebuffer, not the DisplayResolution - mode->vrefresh); - } - - unsafe static ModeInfo* GetModeInfo(LinuxDisplay display, DisplayResolution resolution) - { - for (int i = 0; i < display.pConnector->count_modes; i++) - { - ModeInfo* mode = display.pConnector->modes + i; - if (mode != null && - mode->hdisplay == resolution.Width && - mode->vdisplay == resolution.Height) - { - return mode; - } - } - return null; - } - - #region IDisplayDeviceDriver - - public bool TryChangeResolution(DisplayDevice device, DisplayResolution resolution) - { - unsafe - { - LinuxDisplay display = (LinuxDisplay)device.Id; - ModeInfo* mode = GetModeInfo(display, resolution); - int connector_id = display.pConnector->connector_id; - if (mode != null) - { - return Drm.ModeSetCrtc(FD, display.Id, 0, 0, 0, - &connector_id, 1, mode) == 0; - } - return false; - } - } - - public bool TryRestoreResolution(DisplayDevice device) - { - unsafe - { - LinuxDisplay display = (LinuxDisplay)device.Id; - ModeInfo mode = display.OriginalMode; - int connector_id = display.pConnector->connector_id; - return Drm.ModeSetCrtc(FD, display.Id, 0, 0, 0, - &connector_id, 1, &mode) == 0; - } - } - - #endregion - } -} - diff --git a/testDrm/src/Linux/LinuxFactory.cs b/testDrm/src/Linux/LinuxFactory.cs deleted file mode 100644 index 37d108a2..00000000 --- a/testDrm/src/Linux/LinuxFactory.cs +++ /dev/null @@ -1,253 +0,0 @@ -#region License -// -// LinuxFactory.cs -// -// Author: -// Stefanos A. -// -// Copyright (c) 2006-2014 Stefanos Apostolopoulos -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -#endregion - -using System; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using OpenTK.Graphics; -using OpenTK.Input; -using OpenTK.Platform.Egl; - -namespace OpenTK.Platform.Linux -{ - using Egl = OpenTK.Platform.Egl.Egl; - - // Linux KMS platform - class LinuxFactory : PlatformFactoryBase - { - int _fd; - IntPtr gbm_device; - IntPtr egl_display; - - IJoystickDriver2 JoystickDriver; - LinuxInput MouseKeyboardDriver; - - const string gpu_path = "/dev/dri"; // card0, card1, ... - - public LinuxFactory() - { - Debug.Print("[KMS] Using Linux/KMS backend."); - } - - #region Private Members - - int gpu_fd - { - get - { - lock (this) - { - if (_fd == 0) - { - _fd = CreateDisplay(out gbm_device, out egl_display); - } - return _fd; - } - } - } - - static int CreateDisplay(out IntPtr gbm_device, out IntPtr egl_display) - { - // Query all GPUs until we find one that has a connected display. - // This is necessary in multi-gpu systems, where only one GPU - // can output a signal. - // Todo: allow OpenTK to drive multiple GPUs - // Todo: allow OpenTK to run on an offscreen GPU - // Todo: allow the user to pick a GPU - int fd = 0; - gbm_device = IntPtr.Zero; - egl_display = IntPtr.Zero; - - var files = Directory.GetFiles(gpu_path); - foreach (var gpu in files) - { - if (Path.GetFileName(gpu).StartsWith("card")) - { - int test_fd = SetupDisplay(gpu, out gbm_device, out egl_display); - if (test_fd >= 0) - { - try - { - if (LinuxDisplayDriver.QueryDisplays(test_fd, null)) - { - fd = test_fd; - break; - } - } - catch (Exception e) - { - Debug.WriteLine(e.ToString()); - } - - Debug.Print("[KMS] GPU '{0}' is not connected, skipping.", gpu); - Libc.close(test_fd); - } - } - } - - if (fd == 0) - { - Debug.Print("[Error] No valid GPU found, bailing out."); - throw new PlatformNotSupportedException(); - } - - return fd; - } - - static int SetupDisplay(string gpu, out IntPtr gbm_device, out IntPtr egl_display) - { - Debug.Print("[KMS] Attempting to use gpu '{0}'.", gpu); - - gbm_device = IntPtr.Zero; - egl_display = IntPtr.Zero; - - int fd = Libc.open(gpu, OpenFlags.ReadWrite | OpenFlags.CloseOnExec); - if (fd < 0) - { - Debug.Print("[KMS] Failed to open gpu"); - return fd; - } - Debug.Print("[KMS] GPU '{0}' opened as fd:{1}", gpu, fd); - - gbm_device = Gbm.CreateDevice(fd); - if (gbm_device == IntPtr.Zero) - { - throw new NotSupportedException("[KMS] Failed to create GBM device"); - } - Debug.Print("[KMS] GBM {0:x} created successfully; ", gbm_device); - - egl_display = Egl.GetDisplay(gbm_device); - if (egl_display == IntPtr.Zero) - { - throw new NotSupportedException("[KMS] Failed to create EGL display"); - } - Debug.Print("[KMS] EGL display {0:x} created successfully", egl_display); - - int major, minor; - if (!Egl.Initialize(egl_display, out major, out minor)) - { - ErrorCode error = Egl.GetError(); - throw new NotSupportedException("[KMS] Failed to initialize EGL display. Error code: " + error); - } - Debug.Print("[KMS] EGL {0}.{1} initialized successfully on display {2:x}", major, minor, egl_display); - - return fd; - } - - #endregion - - #region Protected Members - - protected override void Dispose(bool manual) - { - if (egl_display != IntPtr.Zero) - { - Debug.Print("[KMS] Terminating EGL."); - Egl.Terminate(egl_display); - egl_display = IntPtr.Zero; - } - if (gbm_device != IntPtr.Zero) - { - Debug.Print("[KMS] Destroying GBM device."); - Gbm.DestroyDevice(gbm_device); - gbm_device = IntPtr.Zero; - } - if (_fd >= 0) - { - Debug.Print("[KMS] Closing GPU fd."); - Libc.close(_fd); - } - - base.Dispose(manual); - } - - #endregion - - #region IPlatformFactory Members - - public override INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice display_device) - { - return new LinuxNativeWindow(egl_display, gbm_device, gpu_fd, x, y, width, height, title, mode, options, display_device); - } - - public override IDisplayDeviceDriver CreateDisplayDeviceDriver() - { - return new LinuxDisplayDriver(gpu_fd); - } - - public override IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) - { - return new LinuxGraphicsContext(mode, (LinuxWindowInfo)window, shareContext, major, minor, flags); - } - - public override GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext() - { - return (GraphicsContext.GetCurrentContextDelegate)delegate - { - return new ContextHandle(Egl.GetCurrentContext()); - }; - } - - public override IKeyboardDriver2 CreateKeyboardDriver() - { - lock (this) - { - MouseKeyboardDriver = MouseKeyboardDriver ?? new LinuxInput(); - return MouseKeyboardDriver; - } - } - - public override IMouseDriver2 CreateMouseDriver() - { - lock (this) - { - MouseKeyboardDriver = MouseKeyboardDriver ?? new LinuxInput(); - return MouseKeyboardDriver; - } - } - - public override IJoystickDriver2 CreateJoystickDriver() - { - lock (this) - { - JoystickDriver = JoystickDriver ?? new LinuxJoystick(); - return JoystickDriver; - } - } - - public override OpenTK.Input.IGamePadDriver CreateGamePadDriver() - { - return new MappedGamePadDriver(); - } - - #endregion - } -} - diff --git a/testDrm/src/Linux/LinuxGraphicsContext.cs b/testDrm/src/Linux/LinuxGraphicsContext.cs deleted file mode 100644 index 779fd36d..00000000 --- a/testDrm/src/Linux/LinuxGraphicsContext.cs +++ /dev/null @@ -1,302 +0,0 @@ -#region License -// -// LinuxGraphicsContext.cs -// -// Author: -// thefiddler -// -// Copyright (c) 2006-2014 -// -// 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 OpenTK.Graphics; - -namespace OpenTK.Platform.Linux -{ - /// \internal - /// - /// Defines an IGraphicsContext implementation for the Linux KMS framebuffer. - /// For Linux/X11 and other Unix operating systems, use the more generic - /// instead. - /// - /// - /// Note: to display our results, we need to allocate a GBM framebuffer - /// and point the scanout address to that via Drm.ModeSetCrtc. - /// - class LinuxGraphicsContext : Egl.EglUnixContext - { - BufferObject bo, bo_next; - int fd; - bool is_flip_queued; - int swap_interval; - - public LinuxGraphicsContext(GraphicsMode mode, LinuxWindowInfo window, IGraphicsContext sharedContext, - int major, int minor, GraphicsContextFlags flags) - : base(mode, window, sharedContext, major, minor, flags) - { - if (mode.Buffers < 1) - throw new ArgumentException(); - fd = window.FD; - - PageFlip = HandlePageFlip; - PageFlipPtr = Marshal.GetFunctionPointerForDelegate(PageFlip); - } - - public override void SwapBuffers() - { - base.SwapBuffers(); - - if (is_flip_queued) - { - // Todo: if we don't wait for the page flip, - // we drop all rendering buffers and get a crash - // in Egl.SwapBuffers(). We need to fix that - // before we can disable vsync. - WaitFlip(true); // WaitFlip(SwapInterval > 0) - if (is_flip_queued) - { - Debug.Print("[KMS] Dropping frame"); - return; - } - } - - bo_next = LockSurface(); - int fb = GetFramebuffer(bo_next); - QueueFlip(fb); - } - - public override void Update(IWindowInfo window) - { - WaitFlip(true); - - base.SwapBuffers(); - - bo = LockSurface(); - int fb = GetFramebuffer(bo); - SetScanoutRegion(fb); - } - - public override int SwapInterval - { - get - { - return swap_interval; - } - set - { - // We only support a SwapInterval of 0 (immediate) - // or 1 (vsynced). - // Todo: add support for SwapInterval of -1 (adaptive). - // This requires a small change in WaitFlip(). - swap_interval = MathHelper.Clamp(value, 0, 1); - } - } - - void WaitFlip(bool block) - { - PollFD fds = new PollFD(); - fds.fd = fd; - fds.events = PollFlags.In; - - EventContext evctx = new EventContext(); - evctx.version = EventContext.Version; - evctx.page_flip_handler = PageFlipPtr; - - int timeout = block ? -1 : 0; - - while (is_flip_queued) - { - fds.revents = 0; - if (Libc.poll(ref fds, 1, timeout) < 0) - break; - - if ((fds.revents & (PollFlags.Hup | PollFlags.Error)) != 0) - break; - - if ((fds.revents & PollFlags.In) != 0) - Drm.HandleEvent(fd, ref evctx); - else - break; - } - - // Page flip has taken place, update buffer objects - if (!is_flip_queued) - { - IntPtr gbm_surface = WindowInfo.Handle; - Gbm.ReleaseBuffer(gbm_surface, bo); - bo = bo_next; - } - } - - void QueueFlip(int buffer) - { - LinuxWindowInfo wnd = WindowInfo as LinuxWindowInfo; - if (wnd == null) - throw new InvalidOperationException(); - - unsafe - { - int ret = Drm.ModePageFlip(fd, wnd.DisplayDevice.Id, buffer, - PageFlipFlags.FlipEvent, IntPtr.Zero); - - if (ret < 0) - { - Debug.Print("[KMS] Failed to enqueue framebuffer flip. Error: {0}", ret); - } - - is_flip_queued = true; - } - } - - void SetScanoutRegion(int buffer) - { - LinuxWindowInfo wnd = WindowInfo as LinuxWindowInfo; - if (wnd == null) - throw new InvalidOperationException(); - - unsafe - { - ModeInfo* mode = wnd.DisplayDevice.pConnector->modes; - int connector_id = wnd.DisplayDevice.pConnector->connector_id; - int crtc_id = wnd.DisplayDevice.Id; - - int x = 0; - int y = 0; - int connector_count = 1; - int ret = Drm.ModeSetCrtc(fd, crtc_id, buffer, x, y, - &connector_id, connector_count, mode); - - if (ret != 0) - { - Debug.Print("[KMS] Drm.ModeSetCrtc{0}, {1}, {2}, {3}, {4:x}, {5}, {6:x}) failed. Error: {7}", - fd, crtc_id, buffer, x, y, (IntPtr)connector_id, connector_count, (IntPtr)mode, ret); - } - } - } - - BufferObject LockSurface() - { - IntPtr gbm_surface = WindowInfo.Handle; - return Gbm.LockFrontBuffer(gbm_surface); - } - - int GetFramebuffer(BufferObject bo) - { - if (bo == BufferObject.Zero) - goto fail; - - int bo_handle = bo.Handle; - if (bo_handle == 0) - { - Debug.Print("[KMS] Gbm.BOGetHandle({0:x}) failed.", bo); - goto fail; - } - - int width = bo.Width; - int height = bo.Height; - int bpp = Mode.ColorFormat.BitsPerPixel; - int depth = Mode.Depth; - int stride = bo.Stride; - - if (width == 0 || height == 0 || bpp == 0) - { - Debug.Print("[KMS] Invalid framebuffer format: {0}x{1} {2} {3} {4}", - width, height, stride, bpp, depth); - goto fail; - } - - int buffer; - int ret = Drm.ModeAddFB( - fd, width, height, - (byte)depth, (byte)bpp, stride, bo_handle, - out buffer); - if (ret != 0) - { - Debug.Print("[KMS] Drm.ModeAddFB({0}, {1}, {2}, {3}, {4}, {5}, {6}) failed. Error: {7}", - fd, width, height, depth, bpp, stride, bo_handle, ret); - goto fail; - } - - bo.SetUserData((IntPtr)buffer, DestroyFB); - return buffer; - - fail: - Debug.Print("[Error] Failed to create framebuffer."); - return -1; - } - - readonly IntPtr PageFlipPtr; - readonly PageFlipCallback PageFlip; - void HandlePageFlip(int fd, - int sequence, - int tv_sec, - int tv_usec, - IntPtr user_data) - { - is_flip_queued = false; - } - - static readonly DestroyUserDataCallback DestroyFB = HandleDestroyFB; - static void HandleDestroyFB(BufferObject bo, IntPtr data) - { - IntPtr gbm = bo.Device; - int fb = data.ToInt32(); - Debug.Print("[KMS] Destroying framebuffer {0}", fb); - - if (fb != 0) - { - Drm.ModeRmFB(Gbm.DeviceGetFD(gbm), fb); - } - } - - protected override void Dispose(bool manual) - { - if (manual) - { - // Reset the scanout region - LinuxWindowInfo wnd = WindowInfo as LinuxWindowInfo; - if (wnd != null) - { - unsafe - { - int connector_id = wnd.DisplayDevice.pConnector->connector_id; - ModeInfo mode = wnd.DisplayDevice.OriginalMode; - Drm.ModeSetCrtc(fd, - wnd.DisplayDevice.pCrtc->crtc_id, - wnd.DisplayDevice.pCrtc->buffer_id, - wnd.DisplayDevice.pCrtc->x, - wnd.DisplayDevice.pCrtc->y, - &connector_id, - 1, - &mode); - } - } - } - base.Dispose(manual); - } - } -} - - - diff --git a/testDrm/src/Linux/LinuxInput.cs b/testDrm/src/Linux/LinuxInput.cs deleted file mode 100644 index ce5457c3..00000000 --- a/testDrm/src/Linux/LinuxInput.cs +++ /dev/null @@ -1,721 +0,0 @@ -#region License -// -// LinuxKeyboardLibInput.cs -// -// Author: -// Stefanos A. -// -// Copyright (c) 2006-2014 Stefanos Apostolopoulos -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -#endregion - -using System; -using System.Collections.Generic; -using System.Diagnostics; -#if !MINIMAL -using System.Drawing; -#endif -using System.Runtime.InteropServices; -using System.Threading; -using OpenTK.Input; - -namespace OpenTK.Platform.Linux -{ - class LinuxInput : IKeyboardDriver2, IMouseDriver2, IDisposable - { - class DeviceBase - { - readonly IntPtr Device; - string name; - string output; - string logical_seat; - string physical_seat; - - public DeviceBase(IntPtr device, int id) - { - Device = device; - Id = id; - } - - public int Id - { - get - { - return GetId(Device); - } - set - { - LibInput.DeviceSetData(Device, (IntPtr)value); - } - } - - public string Name - { - get - { - name = name ?? LibInput.DeviceGetName(Device); - return name; - } - } - - public IntPtr Seat - { - get - { - return LibInput.DeviceGetSeat(Device); - } - } - - public string LogicalSeatName - { - get - { - logical_seat = logical_seat ?? LibInput.SeatGetLogicalName(Seat); - return logical_seat; - } - } - - public string PhysicalSeatName - { - get - { - physical_seat = physical_seat ?? LibInput.SeatGetPhysicalName(Seat); - return physical_seat; - } - } - - public string Output - { - get - { - output = output ?? LibInput.DeviceGetOutputName(Device); - return output; - } - } - } - - class KeyboardDevice : DeviceBase - { - public KeyboardState State; - - public KeyboardDevice(IntPtr device, int id) - : base(device, id) - { - } - } - - class MouseDevice : DeviceBase - { - public MouseState State; - - public MouseDevice(IntPtr device, int id) - : base(device, id) - { - } - } - - static readonly object Sync = new object(); - static readonly Key[] KeyMap = Evdev.KeyMap; - static long DeviceFDCount; - - // libinput returns various devices with keyboard/pointer even though - // they are not traditional keyboards/mice (for example "Integrated Camera" - // can be detected as a keyboard.) - // Since there is no API to retrieve actual device capabilities, - // we add all detected devices to a "candidate" list and promote them - // to an actual keyboard/mouse only when we receive a valid input event. - // This is far from optimal, but it appears to be the only viable solution - // unless a new API is added to libinput. - DeviceCollection KeyboardCandidates = new DeviceCollection(); - DeviceCollection MouseCandidates = new DeviceCollection(); - DeviceCollection Keyboards = new DeviceCollection(); - DeviceCollection Mice = new DeviceCollection(); - - // 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) - Vector2 CursorOffset = Vector2.Zero; - - IntPtr udev; - IntPtr input_context; - InputInterface input_interface = new InputInterface( - OpenRestricted, CloseRestricted); - int fd; - Thread input_thread; - long exit; - - public LinuxInput() - { - Debug.Print("[Linux] Initializing {0}", GetType().Name); - Debug.Indent(); - try - { - Semaphore ready = new Semaphore(0, 1); - input_thread = new Thread(InputThreadLoop); - input_thread.IsBackground = true; - input_thread.Start(ready); - - // Wait until the input thread is ready. - // Note: it would be nicer if we could avoid this. - // however we need to marshal errors back to the caller - // as exceptions. - // Todo: in a future version, we should add an "Application" object - // to handle all communication with the OS (including event processing.) - // Once we do that, we can remove all separate input threads. - ready.WaitOne(); - if (exit != 0) - { - throw new NotSupportedException(); - } - } - finally - { - Debug.Print("Initialization {0}", exit == 0 ? - "complete" : "failed"); - Debug.Unindent(); - } - } - - #region Private Members - - static CloseRestrictedCallback CloseRestricted = CloseRestrictedHandler; - static void CloseRestrictedHandler(int fd, IntPtr data) - { - Debug.Print("[Input] Closing fd {0}", fd); - int ret = Libc.close(fd); - - if (ret < 0) - { - Debug.Print("[Input] Failed to close fd {0}. Error: {1}", fd, ret); - } - else - { - Interlocked.Decrement(ref DeviceFDCount); - } - } - - static OpenRestrictedCallback OpenRestricted = OpenRestrictedHandler; - static int OpenRestrictedHandler(IntPtr path, int flags, IntPtr data) - { - int fd = Libc.open(path, (OpenFlags)flags); - Debug.Print("[Input] Opening '{0}' with flags {1}. fd:{2}", - Marshal.PtrToStringAnsi(path), (OpenFlags)flags, fd); - - if (fd >= 0) - { - Interlocked.Increment(ref DeviceFDCount); - } - - return fd; - } - - void InputThreadLoop(object semaphore) - { - Debug.Print("[Input] Running on thread {0}", Thread.CurrentThread.ManagedThreadId); - Setup(); - - // Inform the parent thread that initialization has completed successfully - (semaphore as Semaphore).Release(); - Debug.Print("[Input] Released main thread.", input_context); - - // Use a blocking poll for input messages, in order to reduce CPU usage - PollFD poll_fd = new PollFD(); - poll_fd.fd = fd; - poll_fd.events = PollFlags.In; - Debug.Print("[Input] Created PollFD({0}, {1})", poll_fd.fd, poll_fd.events); - - Debug.Print("[Input] Entering input loop.", poll_fd.fd, poll_fd.events); - while (Interlocked.Read(ref exit) == 0) - { - int ret = Libc.poll(ref poll_fd, 1, -1); - ErrorNumber error = (ErrorNumber)Marshal.GetLastWin32Error(); - bool is_error = - 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); - } - - if (is_error) - { - Debug.Print("[Input] Exiting input loop {0} due to poll error [ret:{1} events:{2}]. Error: {3}.", - input_thread.ManagedThreadId, ret, poll_fd.revents, error); - Interlocked.Increment(ref exit); - } - } - 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 UpdateCursor() - { - Point p = new Point( - (int)Math.Round(CursorPosition.X + CursorOffset.X), - (int)Math.Round(CursorPosition.Y + CursorOffset.Y)); - - DisplayDevice display = DisplayDevice.FromPoint(p.X, p.Y) ?? DisplayDevice.Default; - if (display != null) - { - LinuxDisplay d = (LinuxDisplay)display.Id; - Drm.MoveCursor(d.FD, d.Id, p.X, p.Y); - } - } - - void Setup() - { - // Todo: add static path fallback when udev is not installed. - udev = Udev.New(); - if (udev == IntPtr.Zero) - { - Debug.Print("[Input] Udev.New() failed."); - Interlocked.Increment(ref exit); - return; - } - Debug.Print("[Input] Udev.New() = {0:x}", udev); - - input_context = LibInput.CreateContext(input_interface, IntPtr.Zero, udev); - if (input_context == IntPtr.Zero) - { - Debug.Print("[Input] LibInput.CreateContext({0:x}) failed.", udev); - Interlocked.Increment(ref exit); - return; - } - Debug.Print("[Input] LibInput.CreateContext({0:x}) = {1:x}", udev, input_context); - - string seat_id = "seat0"; - int seat_assignment = LibInput.AssignSeat(input_context, seat_id); - if (seat_assignment == -1) - { - Debug.Print("[Input] LibInput.AssignSeat({0:x}) = {1} failed.", input_context, seat_id); - Interlocked.Increment(ref exit); - return; - } - Debug.Print("[Input] LibInput.AssignSeat({0:x}) = {1}", input_context, seat_id); - - fd = LibInput.GetFD(input_context); - if (fd < 0) - { - Debug.Print("[Input] LibInput.GetFD({0:x}) failed.", input_context); - Interlocked.Increment(ref exit); - return; - } - Debug.Print("[Input] LibInput.GetFD({0:x}) = {1}.", input_context, fd); - - ProcessEvents(input_context); - LibInput.Resume(input_context); - Debug.Print("[Input] LibInput.Resume({0:x})", input_context); - - if (Interlocked.Read(ref DeviceFDCount) <= 0) - { - Debug.Print("[Error] Failed to open any input devices."); - Debug.Print("[Error] Ensure that you have access to '/dev/input/event*'."); - Interlocked.Increment(ref exit); - } - } - - void ProcessEvents(IntPtr input_context) - { - // Process all events in the event queue - while (true) - { - // Data available - int ret = LibInput.Dispatch(input_context); - if (ret != 0) - { - Debug.Print("[Input] LibInput.Dispatch({0:x}) failed. Error: {1}", - input_context, ret); - break; - } - - IntPtr pevent = LibInput.GetEvent(input_context); - if (pevent == IntPtr.Zero) - { - break; - } - - IntPtr device = LibInput.GetDevice(pevent); - InputEventType type = LibInput.GetEventType(pevent); - - lock (Sync) - { - switch (type) - { - case InputEventType.DeviceAdded: - HandleDeviceAdded(input_context, device); - break; - - case InputEventType.DeviceRemoved: - HandleDeviceRemoved(input_context, device); - break; - - case InputEventType.KeyboardKey: - HandleKeyboard(GetKeyboard(device), LibInput.GetKeyboardEvent(pevent)); - break; - - case InputEventType.PointerAxis: - HandlePointerAxis(GetMouse(device), LibInput.GetPointerEvent(pevent)); - break; - - case InputEventType.PointerButton: - HandlePointerButton(GetMouse(device), LibInput.GetPointerEvent(pevent)); - break; - - case InputEventType.PointerMotion: - HandlePointerMotion(GetMouse(device), LibInput.GetPointerEvent(pevent)); - break; - - case InputEventType.PointerMotionAbsolute: - HandlePointerMotionAbsolute(GetMouse(device), LibInput.GetPointerEvent(pevent)); - break; - } - } - - LibInput.DestroyEvent(pevent); - } - } - - void HandleDeviceAdded(IntPtr context, IntPtr device) - { - if (LibInput.DeviceHasCapability(device, DeviceCapability.Keyboard)) - { - KeyboardDevice keyboard = new KeyboardDevice(device, Keyboards.Count); - KeyboardCandidates.Add(keyboard.Id, keyboard); - Debug.Print("[Input] Added keyboard device {0} '{1}' on '{2}' ('{3}')", - keyboard.Id, keyboard.Name, keyboard.LogicalSeatName, keyboard.PhysicalSeatName); - } - - if (LibInput.DeviceHasCapability(device, DeviceCapability.Mouse)) - { - MouseDevice mouse = new MouseDevice(device, Mice.Count); - MouseCandidates.Add(mouse.Id, mouse); - Debug.Print("[Input] Added mouse device {0} '{1}' on '{2}' ('{3}')", - mouse.Id, mouse.Name, mouse.LogicalSeatName, mouse.PhysicalSeatName); - } - - if (LibInput.DeviceHasCapability(device, DeviceCapability.Touch)) - { - Debug.Print("[Input] Todo: touch device."); - } - } - - void HandleDeviceRemoved(IntPtr context, IntPtr device) - { - if (LibInput.DeviceHasCapability(device, DeviceCapability.Keyboard)) - { - int id = GetId(device); - Keyboards.TryRemove(id); - KeyboardCandidates.TryRemove(id); - } - - if (LibInput.DeviceHasCapability(device, DeviceCapability.Mouse)) - { - int id = GetId(device); - Mice.TryRemove(id); - MouseCandidates.TryRemove(id); - } - } - - void HandleKeyboard(KeyboardDevice device, KeyboardEvent e) - { - if (device != null) - { - device.State.SetIsConnected(true); - Debug.Print("[Input] Added keyboard {0}", device.Id); - - Key key = Key.Unknown; - uint raw = e.Key; - if (raw >= 0 && raw < KeyMap.Length) - { - key = KeyMap[raw]; - } - - if (key == Key.Unknown) - { - Debug.Print("[Linux] Unknown key with code '{0}'", raw); - } - - device.State.SetKeyState(key, e.KeyState == KeyState.Pressed); - } - } - - void HandlePointerAxis(MouseDevice mouse, PointerEvent e) - { - if (mouse != null) - { - mouse.State.SetIsConnected(true); - - if (e.HasAxis(PointerAxis.HorizontalScroll)) - { - mouse.State.SetScrollRelative((float)e.AxisValue(PointerAxis.HorizontalScroll), 0); - } - if (e.HasAxis(PointerAxis.VerticalScroll)) - { - mouse.State.SetScrollRelative(0, (float)e.AxisValue(PointerAxis.VerticalScroll)); - } - } - } - - void HandlePointerButton(MouseDevice mouse, PointerEvent e) - { - if (mouse != null) - { - mouse.State.SetIsConnected(true); - - MouseButton button = Evdev.GetMouseButton(e.Button); - ButtonState state = e.ButtonState; - mouse.State[(MouseButton)button] = state == ButtonState.Pressed; - } - } - - void HandlePointerMotion(MouseDevice mouse, PointerEvent e) - { - Vector2 delta = new Vector2((float)e.DeltaX, (float)e.DeltaY); - if (mouse != null) - { - mouse.State.SetIsConnected(true); - mouse.State.Position += delta; - } - - CursorPosition = new Vector2( - MathHelper.Clamp(CursorPosition.X + delta.X, bounds.Left, bounds.Right - 1), - MathHelper.Clamp(CursorPosition.Y + delta.Y, bounds.Top, bounds.Bottom - 1)); - UpdateCursor(); - } - - void HandlePointerMotionAbsolute(MouseDevice mouse, PointerEvent e) - { - if (mouse != null) - { - mouse.State.SetIsConnected(true); - mouse.State.Position = new Vector2((float)e.X, (float)e.Y); - } - - CursorPosition = new Vector2( - (float)e.TransformedX(bounds.Width), - (float)e.TransformedY(bounds.Height)); - UpdateCursor(); - } - - static int GetId(IntPtr device) - { - return LibInput.DeviceGetData(device).ToInt32(); - } - - KeyboardDevice GetKeyboard(IntPtr device) - { - int id = GetId(device); - KeyboardDevice keyboard = KeyboardCandidates.FromHardwareId(id); - if (keyboard != null) - { - Keyboards.Add(id, keyboard); - } - else - { - Debug.Print("[Input] Keyboard {0} does not exist in device list.", id); - } - return keyboard; - } - - MouseDevice GetMouse(IntPtr device) - { - int id = GetId(device); - MouseDevice mouse = MouseCandidates.FromHardwareId(id); - if (mouse != null) - { - Mice.Add(id, mouse); - } - else - { - Debug.Print("[Input] Mouse {0} does not exist in device list.", id); - } - return mouse; - } - - #endregion - - #region IKeyboardDriver2 implementation - - KeyboardState IKeyboardDriver2.GetState() - { - lock (Sync) - { - KeyboardState state = new KeyboardState(); - foreach (KeyboardDevice keyboard in Keyboards) - { - state.MergeBits(keyboard.State); - } - return state; - } - } - - KeyboardState IKeyboardDriver2.GetState(int index) - { - lock (Sync) - { - KeyboardDevice device = Keyboards.FromIndex(index); - if (device != null) - { - return device.State; - } - else - { - return new KeyboardState(); - } - } - } - - string IKeyboardDriver2.GetDeviceName(int index) - { - lock (Sync) - { - KeyboardDevice device = Keyboards.FromIndex(index); - if (device != null) - { - return device.Name; - } - else - { - return String.Empty; - } - } - } - - #endregion - - #region IMouseDriver2 implementation - - MouseState IMouseDriver2.GetState() - { - lock (Sync) - { - MouseState state = new MouseState(); - foreach (MouseDevice mouse in Mice) - { - state.MergeBits(mouse.State); - } - return state; - } - } - - MouseState IMouseDriver2.GetState(int index) - { - lock (Sync) - { - MouseDevice device = Mice.FromIndex(index); - if (device != null) - { - return device.State; - } - else - { - return new MouseState(); - } - } - } - - void IMouseDriver2.SetPosition(double x, double y) - { - // Todo: this does not appear to be supported in libinput. - // We will have to emulate this in the KMS mouse rendering code. - CursorOffset = new Vector2( - (float)x - CursorPosition.X, - (float)y - CursorPosition.Y); - UpdateCursor(); - } - - MouseState IMouseDriver2.GetCursorState() - { - MouseState state = (this as IMouseDriver2).GetState(); - state.Position = CursorPosition + CursorOffset; - return state; - } - - #endregion - - #region IDisposable implementation - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - void Dispose(bool disposing) - { - if (disposing) - { - if (input_context != IntPtr.Zero) - { - Debug.Print("[Input] Destroying libinput context"); - LibInput.Suspend(input_context); - Interlocked.Increment(ref exit); - - LibInput.DestroyContext(input_context); - input_context = IntPtr.Zero; - } - - if (udev != IntPtr.Zero) - { - Debug.Print("[Input] Destroying udev context"); - Udev.Destroy(udev); - udev = IntPtr.Zero; - } - - input_interface = null; - } - else - { - Debug.Print("[Input] {0} leaked. Did you forget to call Dispose()?", GetType().FullName); - } - } - - ~LinuxInput() - { - Dispose(false); - } - - #endregion - } -} - diff --git a/testDrm/src/Linux/LinuxJoystick.cs b/testDrm/src/Linux/LinuxJoystick.cs deleted file mode 100644 index fa47bdff..00000000 --- a/testDrm/src/Linux/LinuxJoystick.cs +++ /dev/null @@ -1,533 +0,0 @@ -#region License -// -// The Open Toolkit Library License -// -// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. -// -// 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.IO; -using System.Runtime.InteropServices; -using System.Text; -using OpenTK.Input; - -namespace OpenTK.Platform.Linux -{ - struct AxisInfo - { - public JoystickAxis Axis; - public InputAbsInfo Info; - } - - class LinuxJoystickDetails - { - public Guid Guid; - public string Name; - public int FileDescriptor; - public int PathIndex; // e.g. "0" for "/dev/input/event0". Used as a hardware id - public JoystickState State; - public JoystickCapabilities Caps; - - public readonly Dictionary AxisMap = - new Dictionary(); - public readonly Dictionary ButtonMap = - new Dictionary(); - } - - sealed class LinuxJoystick : IJoystickDriver2 - { - #region Fields - - static readonly HatPosition[,] HatPositions = new HatPosition[,] - { - { HatPosition.UpLeft, HatPosition.Up, HatPosition.UpRight }, - { HatPosition.Left, HatPosition.Centered, HatPosition.Right }, - { HatPosition.DownLeft, HatPosition.Down, HatPosition.DownRight } - }; - - readonly object sync = new object(); - - readonly FileSystemWatcher watcher = new FileSystemWatcher(); - - readonly DeviceCollection Sticks = - new DeviceCollection(); - - bool disposed; - - #endregion - - #region Constructors - - public LinuxJoystick() - { - string path = - Directory.Exists(JoystickPath) ? JoystickPath : - Directory.Exists(JoystickPathLegacy) ? JoystickPathLegacy : - String.Empty; - - if (!String.IsNullOrEmpty(path)) - { - watcher.Path = path; - - watcher.Created += JoystickAdded; - watcher.Deleted += JoystickRemoved; - watcher.EnableRaisingEvents = true; - - OpenJoysticks(path); - } - } - - #endregion - - #region Private Members - - void OpenJoysticks(string path) - { - lock (sync) - { - foreach (string file in Directory.GetFiles(path)) - { - LinuxJoystickDetails stick = OpenJoystick(file); - if (stick != null) - { - Sticks.Add(stick.PathIndex, stick); - } - } - } - } - - int GetJoystickNumber(string path) - { - const string evdev = "event"; - if (path.StartsWith(evdev)) - { - int num; - if (Int32.TryParse(path.Substring(evdev.Length), out num)) - { - return num; - } - } - return -1; - } - - void JoystickAdded(object sender, FileSystemEventArgs e) - { - lock (sync) - { - OpenJoystick(e.FullPath); - } - } - - void JoystickRemoved(object sender, FileSystemEventArgs e) - { - lock (sync) - { - string file = Path.GetFileName(e.FullPath); - int number = GetJoystickNumber(file); - if (number != -1) - { - var stick = Sticks.FromHardwareId(number); - if (stick != null) - { - CloseJoystick(stick); - } - } - } - } - - #endregion - - #region Private Members - - Guid CreateGuid(EvdevInputId id, string name) - { - // Note: the first 8bytes of the Guid are byteswapped - // in three parts when using `new Guid(byte[])`: - // (int, short, short). - // We need to take that into account to match the expected - // Guid in the database. Ugh. - - byte[] bytes = new byte[16]; - - int i = 0; - byte[] bus = BitConverter.GetBytes((int)id.BusType); - bytes[i++] = bus[3]; - bytes[i++] = bus[2]; - bytes[i++] = bus[1]; - bytes[i++] = bus[0]; - - if (id.Vendor != 0 && id.Product != 0 && id.Version != 0) - { - byte[] vendor = BitConverter.GetBytes(id.Vendor); - byte[] product = BitConverter.GetBytes(id.Product); - byte[] version = BitConverter.GetBytes(id.Version); - bytes[i++] = vendor[1]; - bytes[i++] = vendor[0]; - bytes[i++] = 0; - bytes[i++] = 0; - bytes[i++] = product[0]; // no byteswapping - bytes[i++] = product[1]; - bytes[i++] = 0; - bytes[i++] = 0; - bytes[i++] = version[0]; // no byteswapping - bytes[i++] = version[1]; - bytes[i++] = 0; - bytes[i++] = 0; - } - else - { - for (int j = 0; j < bytes.Length - i; j++) - { - bytes[i + j] = (byte)name[j]; - } - } - - return new Guid(bytes); - } - - unsafe static bool TestBit(byte* ptr, int bit) - { - int byte_offset = bit / 8; - int bit_offset = bit % 8; - return (*(ptr + byte_offset) & (1 << bit_offset)) != 0; - } - - unsafe static void QueryCapabilities(LinuxJoystickDetails stick, - byte* axisbit, int axisbytes, - byte* keybit, int keybytes, - out int axes, out int buttons, out int hats) - { - // Note: since we are trying to be compatible with the SDL2 gamepad database, - // we have to match SDL2 behavior bug-for-bug. This means: - // HAT0-HAT3 are all reported as hats (even if the docs say that HAT1 and HAT2 can be analogue triggers) - // DPAD buttons are reported as buttons, not as hats (unlike Windows and Mac OS X) - - axes = buttons = hats = 0; - for (EvdevAxis axis = 0; axis < EvdevAxis.CNT && (int)axis < axisbytes * 8; axis++) - { - InputAbsInfo info; - bool is_valid = true; - is_valid &= TestBit(axisbit, (int)axis); - is_valid &= Evdev.GetAbs(stick.FileDescriptor, axis, out info) >= 0; - if (is_valid) - { - if (axis >= EvdevAxis.HAT0X && axis <= EvdevAxis.HAT3Y) - { - // Analogue hat - stick.AxisMap.Add(axis, new AxisInfo - { - Axis = (JoystickAxis)(JoystickHat)hats++, - Info = info - }); - } - else - { - // Regular axis - stick.AxisMap.Add(axis, new AxisInfo - { - Axis = (JoystickAxis)axes++, - Info = info - }); - } - } - } - - for (EvdevButton button = 0; button < EvdevButton.Last && (int)button < keybytes * 8; button++) - { - if (TestBit(keybit, (int)button)) - { - stick.ButtonMap.Add(button, buttons++); - } - } - } - - LinuxJoystickDetails OpenJoystick(string path) - { - LinuxJoystickDetails stick = null; - - int number = GetJoystickNumber(Path.GetFileName(path)); - if (number >= 0) - { - int fd = -1; - try - { - fd = Libc.open(path, OpenFlags.NonBlock); - if (fd == -1) - return null; - - unsafe - { - const int evsize = Evdev.EventCount / 8; - const int axissize = Evdev.AxisCount / 8; - const int keysize = Evdev.KeyCount / 8; - byte* evbit = stackalloc byte[evsize]; - byte* axisbit = stackalloc byte[axissize]; - byte* keybit = stackalloc byte[keysize]; - - string name; - EvdevInputId id; - - // Ensure this is a joystick device - bool is_valid = true; - - is_valid &= Evdev.GetBit(fd, 0, evsize, new IntPtr(evbit)) >= 0; - is_valid &= Evdev.GetBit(fd, EvdevType.ABS, axissize, new IntPtr(axisbit)) >= 0; - is_valid &= Evdev.GetBit(fd, EvdevType.KEY, keysize, new IntPtr(keybit)) >= 0; - - is_valid &= TestBit(evbit, (int)EvdevType.KEY); - is_valid &= TestBit(evbit, (int)EvdevType.ABS); - is_valid &= TestBit(axisbit, (int)EvdevAxis.X); - is_valid &= TestBit(axisbit, (int)EvdevAxis.Y); - - is_valid &= Evdev.GetName(fd, out name) >= 0; - is_valid &= Evdev.GetId(fd, out id) >= 0; - - if (is_valid) - { - stick = new LinuxJoystickDetails - { - FileDescriptor = fd, - PathIndex = number, - State = new JoystickState(), - Name = name, - Guid = CreateGuid(id, name), - }; - - int axes, buttons, hats; - QueryCapabilities(stick, axisbit, axissize, keybit, keysize, - out axes, out buttons, out hats); - - stick.Caps = new JoystickCapabilities(axes, buttons, hats, false); - - // Poll the joystick once, to initialize its state - PollJoystick(stick); - } - } - - Debug.Print("Found joystick on path {0}", path); - } - catch (Exception e) - { - Debug.Print("Error opening joystick: {0}", e.ToString()); - } - finally - { - if (stick == null && fd != -1) - { - // Not a joystick - Libc.close(fd); - } - } - } - - return stick; - } - - void CloseJoystick(LinuxJoystickDetails js) - { - Sticks.Remove(js.FileDescriptor); - - Libc.close(js.FileDescriptor); - js.FileDescriptor = -1; - js.State = new JoystickState(); // clear joystick state - js.Caps = new JoystickCapabilities(); - } - - JoystickHatState TranslateHat(int x, int y) - { - return new JoystickHatState(HatPositions[x, y]); - } - - void PollJoystick(LinuxJoystickDetails js) - { - unsafe - { - const int EventCount = 32; - InputEvent* events = stackalloc InputEvent[EventCount]; - - long length = 0; - while (true) - { - length = (long)Libc.read(js.FileDescriptor, (void*)events, (UIntPtr)(sizeof(InputEvent) * EventCount)); - if (length <= 0) - break; - - // Only mark the joystick as connected when we actually start receiving events. - // Otherwise, the Xbox wireless receiver will register 4 joysticks even if no - // actual joystick is connected to the receiver. - js.Caps.SetIsConnected(true); - js.State.SetIsConnected(true); - - length /= sizeof(InputEvent); - for (int i = 0; i < length; i++) - { - InputEvent *e = events + i; - switch (e->Type) - { - case EvdevType.ABS: - { - AxisInfo axis; - if (js.AxisMap.TryGetValue((EvdevAxis)e->Code, out axis)) - { - if (axis.Info.Maximum > axis.Info.Minimum) - { - if (e->Code >= (int)EvdevAxis.HAT0X && e->Code <= (int)EvdevAxis.HAT3Y) - { - // We currently treat analogue hats as digital hats - // to maintain compatibility with SDL2. We can do - // better than this, however. - JoystickHat hat = JoystickHat.Hat0 + (e->Code - (int)EvdevAxis.HAT0X) / 2; - JoystickHatState pos = js.State.GetHat(hat); - int xy_axis = (int)axis.Axis & 0x1; - switch (xy_axis) - { - case 0: - // X-axis - pos = TranslateHat( - e->Value.CompareTo(0) + 1, - pos.IsUp ? 0 : pos.IsDown ? 2 : 1); - break; - - case 1: - // Y-axis - pos = TranslateHat( - pos.IsLeft ? 0 : pos.IsRight ? 2 : 1, - e->Value.CompareTo(0) + 1); - break; - } - - js.State.SetHat(hat, pos); - } - else - { - // This axis represents a regular axis or trigger - js.State.SetAxis( - axis.Axis, - (short)Common.HidHelper.ScaleValue(e->Value, - axis.Info.Minimum, axis.Info.Maximum, - short.MinValue, short.MaxValue)); - } - } - } - break; - } - - case EvdevType.KEY: - { - int button; - if (js.ButtonMap.TryGetValue((EvdevButton)e->Code, out button)) - { - js.State.SetButton(button, e->Value != 0); - } - break; - } - } - - // Create a serial number (total seconds in 24.8 fixed point format) - int sec = (int)((long)e->Time.Seconds & 0xffffffff); - int msec = (int)e->Time.MicroSeconds / 1000; - int packet = - ((sec & 0x00ffffff) << 24) | - Common.HidHelper.ScaleValue(msec, 0, 1000, 0, 255); - js.State.SetPacketNumber(packet); - } - } - } - } - - static readonly string JoystickPath = "/dev/input"; - static readonly string JoystickPathLegacy = "/dev"; - - #endregion - - #region IDisposable Members - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - void Dispose(bool manual) - { - if (!disposed) - { - if (manual) - { - } - - watcher.Dispose(); - foreach (LinuxJoystickDetails js in Sticks) - { - CloseJoystick(js); - } - - disposed = true; - } - } - - ~LinuxJoystick() - { - Dispose(false); - } - - #endregion - - #region IJoystickDriver2 Members - - JoystickState IJoystickDriver2.GetState(int index) - { - LinuxJoystickDetails js = Sticks.FromIndex(index); - if (js != null) - { - PollJoystick(js); - return js.State; - } - return new JoystickState(); - } - - JoystickCapabilities IJoystickDriver2.GetCapabilities(int index) - { - LinuxJoystickDetails js = Sticks.FromIndex(index); - if (js != null) - { - return js.Caps; - } - return new JoystickCapabilities(); - } - - Guid IJoystickDriver2.GetGuid(int index) - { - LinuxJoystickDetails js = Sticks.FromIndex(index); - if (js != null) - { - return js.Guid; - } - return Guid.Empty; - } - - #endregion - } -} diff --git a/testDrm/src/Linux/LinuxKeyboardTTY.cs b/testDrm/src/Linux/LinuxKeyboardTTY.cs deleted file mode 100644 index afe66bc1..00000000 --- a/testDrm/src/Linux/LinuxKeyboardTTY.cs +++ /dev/null @@ -1,268 +0,0 @@ -#region License -// -// LinuxKeyboardTTY.cs -// -// Author: -// thefiddler -// -// Copyright (c) 2006-2014 -// -// 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.IO; -using System.Threading; -using OpenTK.Input; - -namespace OpenTK.Platform.Linux -{ - // Todo: this has terrible side-effects on process exit - // (the keyboard remains tied up.) We need to find a - // proper way to clean up after ourselves, even in case - // of a crash. - #if EXPERIMENTAL - class LinuxKeyboardTTY : IKeyboardDriver2, IDisposable - { - const int stdin = 0; // STDIN_FILENO - readonly object sync = new object(); - Thread input_thread; - long exit; - KeyboardState state; - - TerminalState original_state; - TerminalState current_state; - - IntPtr original_mode = new IntPtr(-1); - int original_stdin; - - public LinuxKeyboardTTY() - { - Debug.Print("[Linux] Using TTY keyboard input."); - - if (!SetupTTY(stdin)) - { - throw new NotSupportedException(); - } - - input_thread = new Thread(ProcessEvents); - input_thread.IsBackground = true; - input_thread.Start(); - } - - #region Private Members - - bool SetupTTY(int stdin) - { - // Ensure that we are using a real terminal, - // rather than some short of file redirection.thing. - if (!Terminal.IsTerminal(stdin)) - { - Debug.Print("[Linux] Terminal.IsTerminal({0}) returned false.", stdin); - return false; - } - - //original_stdin = Libc.dup(stdin); - - int ret = Terminal.GetAttributes(stdin, out original_state); - if (ret < 0) - { - Debug.Print("[Linux] Terminal.GetAttributes({0}) failed. Error: {1}", - stdin, ret); - return false; - } - - // Retrieve current keyboard mode - ret = Libc.ioctl(stdin, KeyboardIoctlCode.GetMode, ref original_mode); - if (ret != 0) - { - Debug.Print("[Linux] Libc.ioctl({0}, KeyboardIoctlCode.GetMode) failed. Error: {1}", - stdin, ret); - return false; - } - - // Update terminal state - current_state = original_state; - current_state.LocalMode &= ~(/*LocalFlags.ECHO |*/ LocalFlags.ICANON | LocalFlags.ISIG); - current_state.InputMode &= ~( - InputFlags.ISTRIP | InputFlags.IGNCR | InputFlags.ICRNL | - InputFlags.INLCR | InputFlags.IXOFF | InputFlags.IXON); - current_state.ControlCharacters.VMIN = 0; - current_state.ControlCharacters.VTIME = 0; - Terminal.SetAttributes(stdin, OptionalActions.FLUSH, ref current_state); - - // Request keycodes - int mode = 0x02; // K_MEDIUMRAW - ret = Libc.ioctl(stdin, KeyboardIoctlCode.SetMode, mode); - if (ret != 0) - { - Debug.Print("[Linux] Libc.ioctl({0}, KeyboardIoctlCode.SetMode, {1}) failed. Error: {2}", - stdin, mode, ret); - ExitTTY(this, EventArgs.Empty); - return false; - } - - // Ensure we reset the original keyboard/terminal state on exit, - // even if we crash. - HookEvents(); - - return true; - } - - void ExitTTY(object sender, EventArgs e) - { - if (original_mode != new IntPtr(-1)) - { - Debug.Print("[Linux] Exiting TTY keyboard input."); - - Libc.ioctl(stdin, KeyboardIoctlCode.SetMode, ref original_mode); - Terminal.SetAttributes(stdin, OptionalActions.FLUSH, ref original_state); - original_mode = new IntPtr(-1); - - UnhookEvents(); - } - } - - void HookEvents() - { - Process.GetCurrentProcess().Exited += ExitTTY; - Console.CancelKeyPress += ExitTTY; - } - - void UnhookEvents() - { - Process.GetCurrentProcess().Exited -= ExitTTY; - Console.CancelKeyPress -= ExitTTY; - } - - void ProcessEvents() - { - state.SetIsConnected(true); - - while (Interlocked.Read(ref exit) == 0) - { - byte scancode; - short extended; - - while (Libc.read(stdin, out scancode) > 0) - { - bool pressed = (scancode & 0x80) == 0; - int key = scancode & ~0x80; - KeyModifiers mods; - Debug.Print("{0}:{1} is {2}", key, (int)TranslateKey(key, out mods), pressed); - - if (key == 0) - { - // This is an extended scancode, ignore - Libc.read(stdin, out extended); - } - else - { - lock (sync) - { - state[(Key)key] = pressed; - } - } - - } - } - - input_thread = null; - } - - Key TranslateKey(int key, out KeyModifiers mods) - { - int k = MathHelper.Clamp((int)key, 0, KeyMap.Length); - Key result = KeyMap[k]; - mods = 0; - mods |= (result == Key.AltLeft || result == Key.AltRight) ? KeyModifiers.Alt : 0; - mods |= (result == Key.ControlLeft || result == Key.ControlRight) ? KeyModifiers.Control : 0; - mods |= (result == Key.ShiftLeft || result == Key.ShiftRight) ? KeyModifiers.Shift : 0; - return KeyMap[k]; - } - - static readonly Key[] KeyMap = Evdev.KeyMap; - - #endregion - - #region IKeyboardDriver2 implementation - - public KeyboardState GetState() - { - lock (this) - { - return state; - } - } - - public KeyboardState GetState(int index) - { - lock (this) - { - if (index == 0) - return state; - else - return new KeyboardState(); - } - } - - public string GetDeviceName(int index) - { - if (index == 0) - return "Standard Input"; - else - return String.Empty; - } - - #endregion - - #region IDisposable Implementation - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - void Dispose(bool disposing) - { - Interlocked.Increment(ref exit); - - if (disposing) - { - ExitTTY(this, EventArgs.Empty); - } - else - { - Debug.Print("{0} leaked, did you forget to call Dispose()?", typeof(LinuxKeyboardTTY).FullName); - } - } - - ~LinuxKeyboardTTY() - { - Dispose(false); - } - - #endregion - } - #endif -} - diff --git a/testDrm/src/Linux/LinuxNativeWindow.cs b/testDrm/src/Linux/LinuxNativeWindow.cs deleted file mode 100644 index 46ff13be..00000000 --- a/testDrm/src/Linux/LinuxNativeWindow.cs +++ /dev/null @@ -1,537 +0,0 @@ -#region License -// -// LinuxNativeWindow.cs -// -// Author: -// Stefanos A. -// -// Copyright (c) 2006-2014 Stefanos Apostolopoulos -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -#endregion - -using System; -using System.Diagnostics; -#if !MINIMAL -using System.Drawing; -#endif -using System.Runtime.InteropServices; -using OpenTK.Graphics; -using OpenTK.Input; -using OpenTK.Platform.Egl; - -namespace OpenTK.Platform.Linux -{ - using Egl = OpenTK.Platform.Egl.Egl; - - class LinuxNativeWindow : NativeWindowBase - { - LinuxWindowInfo window; - string title; - Icon icon; - Rectangle bounds; - Size client_size; - bool exists; - bool is_focused; - bool is_cursor_visible = true; - - KeyboardState previous_keyboard; - MouseState previous_mouse; - - MouseCursor cursor_current; - BufferObject cursor_custom; - BufferObject cursor_default; - BufferObject cursor_empty; - - public LinuxNativeWindow(IntPtr display, IntPtr gbm, int fd, - int x, int y, int width, int height, string title, - GraphicsMode mode, GameWindowFlags options, - DisplayDevice display_device) - { - Debug.Print("[KMS] Creating window on display {0:x}", display); - - Title = title; - - display_device = display_device ?? DisplayDevice.Default; - if (display_device == null) - { - throw new NotSupportedException("[KMS] Driver does not currently support headless systems"); - } - - window = new LinuxWindowInfo(display, fd, gbm, display_device.Id as LinuxDisplay); - - // Note: we only support fullscreen windows on KMS. - // We implicitly override the requested width and height - // by the width and height of the DisplayDevice, if any. - width = display_device.Width; - height = display_device.Height; - bounds = new Rectangle(0, 0, width, height); - client_size = bounds.Size; - - if (!mode.Index.HasValue) - { - mode = new EglGraphicsMode().SelectGraphicsMode(window, mode, 0); - } - Debug.Print("[KMS] Selected EGL mode {0}", mode); - - SurfaceFormat format = GetSurfaceFormat(display, mode); - SurfaceFlags usage = SurfaceFlags.Rendering | SurfaceFlags.Scanout; - if (!Gbm.IsFormatSupported(gbm, format, usage)) - { - Debug.Print("[KMS] Failed to find suitable surface format, using XRGB8888"); - format = SurfaceFormat.XRGB8888; - } - - Debug.Print("[KMS] Creating GBM surface on {0:x} with {1}x{2} {3} [{4}]", - gbm, width, height, format, usage); - IntPtr gbm_surface = Gbm.CreateSurface(gbm, - width, height, format, usage); - if (gbm_surface == IntPtr.Zero) - { - throw new NotSupportedException("[KMS] Failed to create GBM surface for rendering"); - } - - window.Handle = gbm_surface; - Debug.Print("[KMS] Created GBM surface {0:x}", window.Handle); - - window.CreateWindowSurface(mode.Index.Value); - Debug.Print("[KMS] Created EGL surface {0:x}", window.Surface); - - cursor_default = CreateCursor(gbm, Cursors.Default); - cursor_empty = CreateCursor(gbm, Cursors.Empty); - Cursor = MouseCursor.Default; - exists = true; - } - - #region Private Members - - static BufferObject CreateCursor(IntPtr gbm, MouseCursor cursor) - { - if (cursor.Width > 64 || cursor.Height > 64) - { - Debug.Print("[KMS] Cursor size {0}x{1} unsupported. Maximum is 64x64.", - cursor.Width, cursor.Height); - return default(BufferObject); - } - - int width = 64; - int height = 64; - SurfaceFormat format = SurfaceFormat.ARGB8888; - SurfaceFlags usage = SurfaceFlags.Cursor64x64 | SurfaceFlags.Write; - - Debug.Print("[KMS] Gbm.CreateBuffer({0:X}, {1}, {2}, {3}, {4}).", - gbm, width, height, format, usage); - - BufferObject bo = Gbm.CreateBuffer( - gbm, width, height, format, usage); - - if (bo == BufferObject.Zero) - { - Debug.Print("[KMS] Failed to create buffer."); - return bo; - } - - // Copy cursor.Data into a new buffer of the correct size - byte[] cursor_data = new byte[width * height * 4]; - for (int y = 0; y < cursor.Height; y++) - { - int dst_offset = y * width * 4; - int src_offset = y * cursor.Width * 4; - int src_length = cursor.Width * 4; - Array.Copy( - cursor.Data, src_offset, - cursor_data, dst_offset, - src_length); - } - bo.Write(cursor_data); - - return bo; - } - - void SetCursor(MouseCursor cursor) - { - BufferObject bo = default(BufferObject); - if (cursor == MouseCursor.Default) - { - bo = cursor_default; - } - else if (cursor == MouseCursor.Empty) - { - bo = cursor_empty; - } - else - { - if (cursor_custom != BufferObject.Zero) - cursor_custom.Dispose(); - cursor_custom = CreateCursor(window.BufferManager, cursor); - bo = cursor_custom; - } - - // If we failed to create a proper cursor, try falling back - // to the empty cursor. We do not want to crash here! - if (bo == BufferObject.Zero) - { - bo = cursor_empty; - } - - if (bo != BufferObject.Zero) - { - Drm.SetCursor(window.FD, window.DisplayDevice.Id, - bo.Handle, bo.Width, bo.Height, cursor.X, cursor.Y); - } - } - - static SurfaceFormat GetSurfaceFormat(IntPtr display, GraphicsMode mode) - { - // Use EGL 1.4 EGL_NATIVE_VISUAL_ID to retrieve - // the corresponding surface format. If that fails - // fall back to a manual algorithm. - int format; - Egl.GetConfigAttrib(display, mode.Index.Value, - Egl.NATIVE_VISUAL_ID, out format); - if ((SurfaceFormat)format != 0) - return (SurfaceFormat)format; - - Debug.Print("[KMS] Failed to retrieve EGL visual from GBM surface. Error: {0}", - Egl.GetError()); - Debug.Print("[KMS] Falling back to hardcoded formats."); - - int r = mode.ColorFormat.Red; - int g = mode.ColorFormat.Green; - int b = mode.ColorFormat.Blue; - int a = mode.ColorFormat.Alpha; - - if (mode.ColorFormat.IsIndexed) - return SurfaceFormat.C8; - if (r == 3 && g == 3 && b == 2 && a == 0) - return SurfaceFormat.RGB332; - if (r == 5 && g == 6 && b == 5 && a == 0) - return SurfaceFormat.RGB565; - if (r == 5 && g == 6 && b == 5 && a == 0) - return SurfaceFormat.RGB565; - if (r == 8 && g == 8 && b == 8 && a == 0) - return SurfaceFormat.RGB888; - if (r == 5 && g == 5 && b == 5 && a == 1) - return SurfaceFormat.RGBA5551; - if (r == 10 && g == 10 && b == 10 && a == 2) - return SurfaceFormat.RGBA1010102; - if (r == 4 && g == 4 && b == 4 && a == 4) - return SurfaceFormat.RGBA4444; - if (r == 8 && g == 8 && b == 8 && a == 8) - 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) - { - // Handle mouse buttons - for (MouseButton i = 0; i < MouseButton.LastButton; i++) - { - if (mouse[i] && !previous_mouse[i]) - { - OnMouseDown(i); - } - - if (!mouse[i] && previous_mouse[i]) - { - OnMouseUp(i); - } - } - - // Handle mouse movement - { - int x = mouse.X; - int y = mouse.Y; - - // Make sure the mouse cannot leave the GameWindow when captured - if (!CursorVisible) - { - x = MathHelper.Clamp(mouse.X, Bounds.Left, Bounds.Right - 1); - y = MathHelper.Clamp(mouse.Y, Bounds.Top, Bounds.Bottom - 1); - if (x != mouse.X || y != mouse.Y) - { - Mouse.SetPosition(x, y); - } - } - - if (x != previous_mouse.X || y != previous_mouse.Y) - { - OnMouseMove(x, y); - } - } - - // Handle mouse scroll - if (mouse.Scroll != previous_mouse.Scroll) - { - float dx = mouse.Scroll.X - previous_mouse.Scroll.X; - float dy = mouse.Scroll.Y - previous_mouse.Scroll.Y; - OnMouseWheel(dx, dy); - } - - // Handle mouse focus - // 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); - } - } - - #endregion - - #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(); - } - - public override void Close() - { - exists = false; - } - - public override Point PointToClient(Point point) - { - var client = Location; - return new Point(point.X - client.X, point.Y - client.Y); - } - - public override Point PointToScreen(Point point) - { - var client = Location; - return new Point(point.X + client.X, point.Y + client.Y); - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - Debug.Print("[KMS] Destroying window {0}.", window.Handle); - Drm.SetCursor(window.FD, window.DisplayDevice.Id, 0, 0, 0, 0, 0); - window.Dispose(); - Gbm.DestroySurface(window.Handle); - } - else - { - Debug.Print("[KMS] {0} leaked. Did you forget to call Dispose()?", GetType().FullName); - } - } - - public override Icon Icon - { - get - { - return icon; - } - set - { - if (icon != value) - { - icon = value; - OnIconChanged(EventArgs.Empty); - } - } - } - - public override string Title - { - get - { - return title; - } - set - { - if (title != value) - { - title = value; - OnTitleChanged(EventArgs.Empty); - } - } - } - - public override bool Focused - { - get - { - return is_focused; - } - } - - public override bool Visible - { - get - { - return true; - } - set - { - } - } - - public override bool Exists - { - get - { - return exists; - } - } - - public override IWindowInfo WindowInfo - { - get - { - return window; - } - } - - public override WindowState WindowState - { - get - { - return WindowState.Fullscreen; - } - set - { - } - } - - public override WindowBorder WindowBorder - { - get - { - return WindowBorder.Hidden; - } - set - { - } - } - - public override Rectangle Bounds - { - get - { - return bounds; - } - set - { - } - } - - public override Size ClientSize - { - get - { - return client_size; - } - set - { - } - } - - public override bool CursorVisible - { - get - { - return is_cursor_visible; - } - set - { - if (value && !is_cursor_visible) - { - SetCursor(cursor_current); - } - else if (!value && is_cursor_visible) - { - SetCursor(MouseCursor.Empty); - } - is_cursor_visible = value; - } - } - - public override MouseCursor Cursor - { - get - { - return cursor_current; - } - set - { - if (cursor_current != value) - { - if (cursor_custom != BufferObject.Zero) - { - cursor_custom.Dispose(); - } - - if (CursorVisible) - { - SetCursor(value); - } - cursor_current = value; - } - } - } - - #endregion - } -} - diff --git a/testDrm/src/Linux/Signal.cs b/testDrm/src/Linux/Signal.cs new file mode 100644 index 00000000..16eca34a --- /dev/null +++ b/testDrm/src/Linux/Signal.cs @@ -0,0 +1,77 @@ +// +// Signals.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Runtime.InteropServices; + +namespace Linux +{ + public enum Signal : int { + SIGHUP = 1, + SIGINT = 2, + SIGQUIT = 3, + SIGILL = 4, + SIGTRAP = 5, + SIGABRT = 6, + SIGIOT = 6, + SIGBUS = 7, + SIGFPE = 8, + SIGKILL = 9, + SIGUSR1 = 10, + SIGSEGV = 11, + SIGUSR2 = 12, + SIGPIPE = 13, + SIGALRM = 14, + SIGTERM = 15, + SIGSTKFLT = 16, + SIGCHLD = 17, + SIGCONT = 18, + SIGSTOP = 19, + SIGTSTP = 20, + SIGTTIN = 21, + SIGTTOU = 22, + SIGURG = 23, + SIGXCPU = 24, + SIGXFSZ = 25, + SIGVTALRM = 26, + SIGPROF = 27, + SIGWINCH = 28, + /// same as SIGPOLL + SIGIO = 29, + SIGPWR = 30, + SIGSYS = 31, + SIGUNUSED = 31 + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void SignalHandler(Signal signal); + + public static class Kernel + { + [DllImport("libc")] + public static extern int signal(Signal s, SignalHandler handler); + } +} + diff --git a/testDrm/src/Linux/LinuxWindowInfo.cs b/testDrm/src/Linux/TTY.cs similarity index 54% rename from testDrm/src/Linux/LinuxWindowInfo.cs rename to testDrm/src/Linux/TTY.cs index e1d91e9a..180a585c 100644 --- a/testDrm/src/Linux/LinuxWindowInfo.cs +++ b/testDrm/src/Linux/TTY.cs @@ -1,11 +1,10 @@ -#region License -// -// LinuxWindowInfo.cs +// +// TTY.cs // // Author: -// Stefanos A. +// Jean-Philippe Bruyère // -// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// Copyright (c) 2013-2017 Jean-Philippe Bruyère // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -24,33 +23,22 @@ // 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 OpenTK.Platform.Egl; +using System.Runtime.InteropServices; -namespace OpenTK.Platform.Linux +namespace Crow.Linux.VT { - class LinuxWindowInfo : EglWindowInfo - { - public int FD { get; private set; } - public LinuxDisplay DisplayDevice { get; private set; } - public IntPtr BufferManager { get; private set; } - - public LinuxWindowInfo(IntPtr display, int fd, IntPtr gbm, LinuxDisplay display_device) - : base(IntPtr.Zero, display, IntPtr.Zero) - { - if (display_device == null) - throw new ArgumentNullException(); + internal class TTY { + [DllImport("libc")] + static extern IntPtr ptsname(int fd); + [DllImport("libc")] + public static extern int unlockpt(int fd); - FD = fd; - BufferManager = gbm; - DisplayDevice = display_device; - // The window handle and surface handle must - // be filled in manually once they are known. - } - } + public static string GetFreePtsPath (int fd){ + IntPtr pStr = ptsname (fd); + string name = Marshal.PtrToStringAnsi (pStr); + return name; + } + } } diff --git a/testDrm/src/Linux/VT.cs b/testDrm/src/Linux/VT.cs new file mode 100644 index 00000000..a9402c85 --- /dev/null +++ b/testDrm/src/Linux/VT.cs @@ -0,0 +1,217 @@ +// +// VT.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Runtime.InteropServices; + +namespace Crow.Linux.VT { + public enum KDMode : byte { + TEXT = 0x00, + GRAPHICS= 0x01, + TEXT0 = 0x02, /* obsolete */ + TEXT1 = 0x03 /* obsolete */ + } + public enum SwitchMode : sbyte { + AUTO = 0x00, /* auto vt switching */ + PROCESS = 0x01, /* process controls switching */ + ACKACQ = 0x02 /* acknowledge switch */ + } + public enum KbdMode{ + RAW = 0x00, + XLATE = 0x01, + MEDIUMRAW = 0x02, + UNICODE = 0x03, + OFF = 0x04, + } + public struct vt_mode { + public SwitchMode mode; /* vt mode */ + public sbyte waitv; /* if set, hang on writes if not active */ + public short relsig; /* signal to raise on release req */ + public short acqsig; /* signal to raise on acquisition */ + public short frsig; /* unused (set to 0) */ + } + public struct State { + public ushort v_active; /* active vt */ + public ushort v_signal; /* signal to send */ + public ushort v_state; /* vt bitmask */ + } + public struct Sizes { + public ushort v_rows; /* number of rows */ + public ushort v_cols; /* number of columns */ + public ushort v_scrollsize; /* number of lines of scrollback */ + } + + public struct KbEntry { + public byte kb_table; + public byte kb_index; + public ushort kb_value; + } + + public class VTControler : IDisposable { + public int fd = -1; + + #region ctor + public VTControler (string path = "/dev/tty") { + fd = Libc.open (path, OpenFlags.ReadWrite); + if (fd <= 0) + throw new Exception ("VTControler: unable to open " + path); + } + #endregion + + /// set Graphic or Text mode for VT. + unsafe public KDMode KDMode { + get { + KDMode m = 0; + if (ioctl (fd, KDGETMODE, ref m) < 0) + throw new Exception ("VTControler: failed to get current TTY mode"); + return m; + } + set { + if (ioctl (fd, KDSETMODE, value) < 0) + throw new Exception ("VTControler: failed to set current TTY mode"); + } + } + /// set AUTO or PROCESS mode for VT. + unsafe public vt_mode VTMode { + get { + vt_mode m = new vt_mode(); + if (ioctl (fd, VT_GETMODE, ref m) < 0) + throw new Exception ("VTControler: failed to get VTMode for current"); + return m; + } + set { + if (ioctl (fd, VT_SETMODE, ref value) < 0) + throw new Exception ("VTControler: failed to set VTMode for current VT"); + } + } + + /// + /// Switchs to V. + /// + /// Virtual Terminal to switch to + /// If set to true wait until switch is completed. + public void SwitchTo (int vt, bool waitCompleted = true){ + if (ioctl (fd, VT_ACTIVATE, vt) < 0) + throw new Exception ("VTControler: failed to activate TTY" + vt); + if (waitCompleted) { + if (ioctl (fd, VT_WAITACTIVE, vt) < 0) + throw new Exception ("VTControler: error waiting for activation of TTY" + vt); + } + } + public State State { + get { + State vtstats = new State (); + if (ioctl (fd, VT_GETSTATE, ref vtstats) < 0) + throw new Exception ("VirtualTerminal: unable to get TTY Stats"); + return vtstats; + } + } + public int FirstAvailableVT { + get { + long freeVT = 0; + ioctl (fd, VT_OPENQRY, ref freeVT); + return (int)freeVT; + } + } + public int CurrentVT { + get { + return (int)State.v_active; + } + } + + const int ACKACQ = 0x02; + + public void AcknoledgeSwitchRequest (){ + if (ioctl (fd, VT_RELDISP, ACKACQ)<0) + throw new Exception ("VTControler: failed to acknowledge switch with VT_RELDISP"); + } + + #region IDisposable implementation + ~VTControler(){ + Dispose (false); + } + public void Dispose () + { + Dispose (true); + GC.SuppressFinalize (this); + } + protected virtual void Dispose (bool disposing){ + if (fd > 0) + Libc.close (fd); + fd = -1; + } + #endregion + + #region IOCTLS constants + const uint KDSETMODE = 0x4B3A; /* set text/graphics mode */ + const uint KDGETMODE = 0x4B3B; /* get current mode */ + + const uint KDGKBMODE = 0x4B44; /* gets current keyboard mode */ + const uint KDSKBMODE = 0x4B45; /* sets current keyboard mode */ + const uint KDGKBENT = 0x4B46; /* gets one entry in translation table */ + const uint KDSKBENT = 0x4B47; /* sets one entry in translation table */ + + const uint VT_OPENQRY = 0x5600; /* find available vt */ + const uint VT_GETMODE = 0x5601; /* get mode of active vt */ + const uint VT_SETMODE = 0x5602; /* set mode of active vt */ + + const uint VT_GETSTATE = 0x5603; /* get global vt state info */ + const uint VT_SENDSIG = 0x5604; /* signal to send to bitmask of vts */ + + const uint VT_RELDISP = 0x5605; /* release display */ + + const uint VT_ACTIVATE = 0x5606; /* make vt active */ + const uint VT_WAITACTIVE= 0x5607; /* wait for vt active */ + const uint VT_DISALLOCATE= 0x5608; /* free memory associated to vt */ + + const uint VT_RESIZE = 0x5609; /* set kernel's idea of screensize */ + #endregion + + #region ioctl overrides + [DllImport("libc")] + static extern int ioctl(int d, uint request, ref KbEntry entry); + [DllImport("libc")] + static extern int ioctl(int d, uint request, ref KbdMode value); + [DllImport("libc")] + static extern int ioctl(int d, uint request, ref long value); + [DllImport("libc")] + static extern int ioctl(int d, uint request, int value); + [DllImport("libc")] + static extern int ioctl(int d, uint request, ref int value); + [DllImport("libc")] + static extern int ioctl(int d, uint request, ref State value); + [DllImport("libc")] + static extern int ioctl(int d, uint request, ref SwitchMode value); + [DllImport("libc")] + static extern int ioctl(int d, uint request, KDMode value); + [DllImport("libc")] + static extern int ioctl(int d, uint request, ref KDMode value); + [DllImport("libc")] + static extern int ioctl(int d, uint request, vt_mode value); + [DllImport("libc")] + static extern int ioctl(int d, uint request, ref vt_mode value); + #endregion + } +} \ No newline at end of file diff --git a/testDrm/testDrm.csproj b/testDrm/testDrm.csproj index 0949f6b6..b033b0bc 100644 --- a/testDrm/testDrm.csproj +++ b/testDrm/testDrm.csproj @@ -18,6 +18,7 @@ + testDrm.Tests true @@ -59,14 +60,18 @@ Code - - Code - - - + + + Code + + + + + + @@ -76,18 +81,7 @@ - - - - - - - Code - - - - - + @@ -95,4 +89,7 @@ Crow + + + \ No newline at end of file diff --git a/testDrm/tests.cs b/testDrm/tests.cs new file mode 100644 index 00000000..026ce199 --- /dev/null +++ b/testDrm/tests.cs @@ -0,0 +1,154 @@ +// +// tests.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using Crow.Linux; +using System.Threading; +using Linux; +using System.Runtime.InteropServices; +using System.Text; + +//using static Crow.Linux.VT.VTControler; +using VT = Crow.Linux.VT; +using Crow.Linux.DRI; + +namespace testDrm +{ + static class Tests + { + static void signal_handler (Signal s){ + Console.WriteLine ("signal catched: " + s.ToString()); + } + static void Main () + { + using (GPUControler gpu = new GPUControler ()) { + } + } + static void signalTest (){ + if (Kernel.signal (Signal.SIGINT, signal_handler) < 0) + throw new Exception ("signal handler registation failed"); + Console.WriteLine ("Handler registered for {0}", Signal.SIGINT); + while (true) + Thread.Sleep (1); + } +// static void pty_tests(){ +// int ret = 0; +// int fd = -1; +// fd = Libc.posix_openpt (OpenFlags.ReadWrite); +// if (fd < 0) +// return; +// string newPts = Crow.Linux.VT.TTY.GetFreePtsPath (fd); +// Console.WriteLine (newPts); +// +// Crow.Linux.VT.TTY.unlockpt (fd); +// int fdPts = -1; +// fdPts = Libc.open(newPts, OpenFlags.ReadWrite); +// if (fdPts < 0) +// return; +// +// Libc.close (fdPts); +// Libc.close (fd); +// +// Console.WriteLine ("terminated succeffully"); +// } + + static void tty_switch2(){ + int previousVT = -1, appVT = -1; + using(VT.VTControler master = new VT.VTControler()){ + VT.vt_mode m = master.VTMode; + + Console.WriteLine ("Startup:"); + Console.WriteLine ("\tVT{0}\t- KDMode: {1}", master.CurrentVT, master.KDMode); + Console.WriteLine ("\t\t- VTMode= {0}", m.mode); + Console.WriteLine ("\t\t- RELSIG= {0}", ((Signal)m.relsig).ToString()); + + previousVT = master.CurrentVT; + appVT = master.FirstAvailableVT; + + + master.SwitchTo (appVT); + + m = master.VTMode; + + try { + master.KDMode = VT.KDMode.GRAPHICS; + //m.mode = VT.SwitchMode.AUTO; + //master.VTMode = m; + + } catch (Exception ex) { + Console.WriteLine (ex.ToString ()); + } + + Console.WriteLine ("Switch:"); + Console.WriteLine ("\tVT{0}\t- KDMode: {1}", master.CurrentVT, master.KDMode); + Console.WriteLine ("\t\t- VTMode= {0}", m.mode); + Console.WriteLine ("\t\t- RELSIG= {0}", m.relsig.ToString()); + + if (Kernel.signal (Signal.SIGUSR1, switch_request_handle) < 0) + throw new Exception ("signal handler registation failed"); + Console.WriteLine ("Handler registered for switching tty"); + if (Kernel.signal (Signal.SIGINT, sigint_handler) < 0) + throw new Exception ("SIGINT handler registation failed"); + Console.WriteLine ("SIGINT Handler registered"); + + while (running) { + Thread.Sleep (500); + Console.Write ("."); + } + + + master.SwitchTo (previousVT); + + Console.WriteLine ("Back to master:"); + Console.WriteLine ("\tVT{0}\t- KDMode: {1}", master.CurrentVT, master.KDMode); + Console.WriteLine ("\t\t- VTMode= {0}", master.VTMode.mode); + + } + +// using (VTControler vt = new VTControler ("/dev/tty" + appVT)) { +// vt.CurrentMode = VT.Mode.GRAPHICS; +// } + + + Console.WriteLine ("terminated succeffully"); + //vtc = new VTControler ("/dev/tty" + appVT); + //vtc.CurrentMode = VT.Mode.GRAPHICS; + } + static bool running = true; + static void switch_request_handle (Signal s){ + Console.WriteLine ("switch request catched: " + s.ToString()); + using (VT.VTControler master = new VT.VTControler ()) { + Libc.write (master.fd, Encoding.ASCII.GetBytes ("this is a test string")); + master.AcknoledgeSwitchRequest (); + } + } + static void sigint_handler (Signal s){ + Console.WriteLine ("SIGINT catched"); + running = false; + } + + } +} + diff --git a/testDrm/ui/menu.crow b/testDrm/ui/menu.crow new file mode 100755 index 00000000..92cd224b --- /dev/null +++ b/testDrm/ui/menu.crow @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file -- 2.47.3