Ejemplo n.º 1
0
        private 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();
        }
Ejemplo n.º 2
0
        Guid IJoystickDriver2.GetGuid(int index)
        {
            LinuxJoystickDetails js = Sticks.FromIndex(index);

            if (js != null)
            {
                return(js.Guid);
            }
            return(Guid.Empty);
        }
Ejemplo n.º 3
0
        JoystickCapabilities IJoystickDriver2.GetCapabilities(int index)
        {
            LinuxJoystickDetails js = Sticks.FromIndex(index);

            if (js != null)
            {
                return(js.Caps);
            }
            return(new JoystickCapabilities());
        }
Ejemplo n.º 4
0
        JoystickState IJoystickDriver2.GetState(int index)
        {
            LinuxJoystickDetails js = Sticks.FromIndex(index);

            if (js != null)
            {
                PollJoystick(js);
                return(js.State);
            }
            return(new JoystickState());
        }
Ejemplo n.º 5
0
 private 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);
             }
         }
     }
 }
Ejemplo n.º 6
0
        private 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 = (int)(JoystickHat)hats++,
                            Info = info
                        });
                    }
                    else
                    {
                        // Regular axis
                        stick.AxisMap.Add(axis, new AxisInfo
                        {
                            Axis = 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++);
                }
            }
        }
Ejemplo n.º 7
0
        private 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;
                    }
                    //As we've received an event, this must be connected!
                    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.
                                        int hat     = (e->Code - (int)EvdevAxis.HAT0X) / 2;
                                        int xy_axis = (int)axis.Axis & 0x1;
                                        int value   = e->Value.CompareTo(0) + 1;
                                        switch (xy_axis)
                                        {
                                        case 0:
                                            // X-axis
                                            js.hatStates[hat, 1] = value;
                                            break;

                                        case 1:
                                            // Y-axis
                                            js.hatStates[hat, 0] = value;
                                            break;
                                        }
                                        js.State.SetHat((JoystickHat)hat, TranslateHat(js.hatStates[hat, 0], js.hatStates[hat, 1]));
                                    }
                                    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);
                    }
                }
            }
        }
Ejemplo n.º 8
0
        private 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);
        }
Ejemplo n.º 9
0
        private void ProcessEvents()
        {
            if (!File.Exists(InputDevicePath))
            {
                //BSD may not have the Linux ProcFS, no use running a pointless loop
                return;
            }

            while (!disposed)
            {
                /*
                 * Rather hacky filesystemwatcher equivilant, as it doesn't work on ProcFS
                 * As the thing is psuedo-files, we can't use the last modified date either
                 * We have to load the thing into a stream and hash. At least it's only a few bytes.....
                 *
                 * Note: We could possibly override the Mono filesystem watcher backend, but this is a global variable only
                 * and so may break user code
                 */
                Thread.Sleep(750); //750ms is the Mono default for the file hashing version of FileSystemWatcher backend
                List <int> connectedDevices;

                using (FileStream stream = new FileStream(InputDevicePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                {
                    string newHash = BitConverter.ToString(Hasher.ComputeHash(stream));
                    if (newHash == procHash)
                    {
                        //Hash of /proc/bus/input/devices has not changed, hence no new devices added / removed
                        continue;
                    }
                    stream.Seek(0, 0);
                    connectedDevices = GetConnectedDevices(stream);
                    procHash         = newHash;
                    OpenJoysticks(EvdevPath);
                }

                List <int> xboxPadCandidates = new List <int>();

                foreach (LinuxJoystickDetails stick in Sticks)
                {
                    if (connectedDevices.Contains(stick.PathIndex))
                    {
                        if (stick.Caps.AxisCount == 6 && stick.Caps.ButtonCount == 11 && stick.Caps.HatCount == 2)
                        {
                            xboxPadCandidates.Add(stick.PathIndex);
                        }
                        else
                        {
                            stick.Caps.SetIsConnected(true);
                            stick.State.SetIsConnected(true);
                        }
                    }
                    else
                    {
                        stick.State.SetIsConnected(false);
                        stick.Caps.SetIsConnected(false);
                    }
                }

                if (xboxPadCandidates.Count < 4)
                {
                    /*
                     * The XBox wireless reciever generates 4 sticks which may not have anything connected to them
                     * If there are less than 4, this can't be this (or it's been fixed evdev side in the meantime)
                     * so let's set them as connected
                     */
                    foreach (int idx in xboxPadCandidates)
                    {
                        LinuxJoystickDetails stick = Sticks.FromHardwareId(idx);
                        if (stick != null)
                        {
                            stick.Caps.SetIsConnected(true);
                            stick.State.SetIsConnected(true);
                        }
                    }
                }
            }
        }
Ejemplo n.º 10
0
        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:
                        {
                            JoystickButton 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);
                    }
                }
            }
        }