C# – XBox 360 Controller library with a sample application

Created an easy to use XBox 360 Controller library in C# (with a sample application) using the SharpDX.XInput managed .NET wrapper of the DirectX API.

 https://github.com/okmer/XBoxController

Screenshot of the sample application.
Screenshot of the sample application.

A BUG in SharpDX.XInput ci-ci217, resulting in issues with the  left Thumb Stick, Left Trigger, and Right Trigger! Please stick to SharpDX.XInput v4.1.0-ci184 for now.

using System;

using Com.Okmer.GameController;
 
namespace XBoxSampleConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            XBoxController controller = new XBoxController();

            //Connection
            controller.Connection.StateChanged += (s, e) => Console.WriteLine($"Connection state: {e.State}");
 
            //Battery
            controller.Battery.LevelChanged += (s, e) => Console.WriteLine($"Battery level: {e.Level}");
 
            //Buttons A, B
            controller.A.StateChanged += (s, e) => Console.WriteLine($"A state: {e.State}");
            controller.B.StateChanged += (s, e) => Console.WriteLine($"B state: {e.State}");
            
            //Buttons D-Pad Up, Down, Left, Right
            controller.Up.StateChanged += (s, e) => Console.WriteLine($"Up state: {e.State}");
            controller.Down.StateChanged += (s, e) => Console.WriteLine($"Down state: {e.State}");

            //Thumb Positions Left, Right
            controller.LeftThumb.PositionsChanged += (s, e) => Console.WriteLine($"Left thumb X: {e.X}, Y: {e.Y}");
            controller.RightThumb.PositionsChanged += (s, e) => Console.WriteLine($"Right thumb X: {e.X}, Y: {e.Y}");
 
            //Rumble at the speed of the trigger position
            controller.LeftTrigger.PositionChanged += (s, e) => controller.LeftRumble.Speed = e.Position;
            controller.RightTrigger.PositionChanged += (s, e) => controller.RightRumble.Speed = e.Position;
 
            //Wait on ENTER to exit...
            Console.ReadLine();
        }
    }
}

C# – MutableWhen Extension for the Task Parallel Library (TPL)

This is a little playful MutableWhenAny and MutableWhenAll extension for the Task Parallel Library (TPL), using a ObservableCollection.

The extension makes it possible to add or removed tasks to/from an ObservableCollection<Task> while the MutableWhenAny or MutableWhenAll is used to wait on the tasks in this (mutable) collection.

 https://github.com/okmer/MutableWhen

It’s not super useful in practice, but a nice little exercise that combines to cool C# features into a fun asynchronous “magic” trick.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Threading;
using System.Threading.Tasks;
 
namespace Com.Okmer.Extensions.ObservableCollectionOfTask
{
    public static class ObservableCollectionOfTaskExtention
    {
        private const int INFINITE = -1;
 
        public static async Task MutableWhenAll(this ObservableCollection<Task> collection)
        {
            await MutableWhenSomething(collection, Task.WhenAll);
        }
 
        public static async Task MutableWhenAny(this ObservableCollection<Task> collection)
        {
            await MutableWhenSomething(collection, Task.WhenAny);
        }
 
        private static async Task MutableWhenSomething(this ObservableCollection<Task> collection, Func<IEnumerable<Task>, Task> whenSomething)
        {
            Task waitAllTask = null;
            Task helperTask = null;
 
            bool isCollectionChanged = false;
 
            do
            {
                //Cancellation on collection changed event
                var cts = new CancellationTokenSource();
                var cancelActionHandler = (sender, arg) => cts.Cancel(false);
                collection.CollectionChanged += cancelActionHandler;
 
                //Current collection
                waitAllTask = whenSomething(collection);
 
                //Wait on current collection or collection changed event
                try
                {
                    helperTask = Task.Delay(INFINITE, cts.Token);
                    await Task.WhenAny(waitAllTask, helperTask);
                }
                finally
                {
                    isCollectionChanged = cts.IsCancellationRequested;
                    cts.Cancel(false);
                    cts.Dispose();
                    collection.CollectionChanged -= cancelActionHandler;
                }
            }
            while (isCollectionChanged);
 
            //Return the WaitAll on collection results
            await waitAllTask;
        }
    }
}

A simple example application that demonstrates the MutableWhenAll extension on an observable collection of tasks. The longest running task is added to the observable collection after MutableWhenAll is called, but the MutableWhenAll will complete only when all tasks (included this longest running task) are completed.

using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
 
using Com.Okmer.Extensions.ObservableCollectionOfTask;
 
namespace MutableWhenAllTest
{
    class Program
    {
        static void Main(string[] args)
        {
            ObservableCollection<Task> tasks = new ObservableCollection<Task>();
 
            Task t1 = Task.Run(async () => { await Task.Delay(1000); Console.WriteLine("t1"); });
            Task t2 = Task.Run(async () => { await Task.Delay(2000); Console.WriteLine("t2"); });
            Task t3 = Task.Run(async () => { await Task.Delay(3000); Console.WriteLine("t3"); });
 
            tasks.Add(t1);
            tasks.Add(t2);
 
            Task a1 = tasks.MutableWhenAll();
 
            tasks.Add(t3);
 
            a1.ContinueWith(t =>
            {
                if (t.IsCanceled)
                {
                    Console.WriteLine("Canceled");
                }
 
                if (t.IsFaulted)
                {
                    Console.WriteLine("Faulted");
                }
 
                if (t.IsCompleted)
                {
                    Console.WriteLine("Completed");
                }
            });
 
            a1.Wait();
 
            Console.ReadLine();
        }
 
    }
}