views:

484

answers:

5

Hi, I've just written a small XBox 360 Wireless Controller managed interface that basically wraps around the low-lever SlimDX wrapper library and provides a easy, managed API for the XBOX 360 controller.

Internally, the class polls the gamepad every N ms, and shoots events as it detects changes in the underlying state of the controller.

I'm experiencing some what dead end with timers that is basiclly forcing to choose between the lesser of two evils:

  • Either make my XBox360GamePad class UI framework specific (i.e. support WPF/WinForms will be hard-coded in the class, and the class has to reference these frameworks...)

  • Make the class completely framework agnostic, but force the users to sprinkle their code with Dispatcher.Invoke / Invoke() calls to be able to update UI according to the events generated.

If I choose the latter option (of making the code UI agnostic), then I basically use the "generic" System.Timers.Timer or any timer that has no UI dependency. In that case I end up having events generated/called from a thread that is incapable of directly updating the UI, i.e. in WPF, I would have to issue every update originated form the 360 controller class through the (ugly) use of Dispatcher.Invoke.

On the other hand, If I use DispatcherTimer inside the XBox 360 Controller class I have a working component that can update the UI directly with no fuss, but now my whole controller class is coupled to WPF, and it can't be used without being dependent on WPF (i.e. in a pure console app)

What I'm kind of looking is a some sort solution that would allow me to be both framework agnostic and also update UI without having to resort to all kinds of Dispatcher.Invoke() techniques... If for example there was a shared base class for all timers, I could somehow inject the timer as a dependency according to the relevant scenario.. Has anyone ever dealt successfully with this sort of problem?

+1  A: 

Is a polling architecture the only option?

In any case, personally I would restructure the system so that outside world can subscribe to events that is fired from the controller class.

If you want the controller to fire the events on the right thread context, then I would add a property for a ISynchronizeInvoke interface, and if this property is non-null inside the controller, use the interface, otherwise just invoke the events directly, on the thread the controller runs on.

This would for instance allow you make the entire polling thing into a thread that wakes up every N milliseconds to do its job, or even use event objects to just wait for events (if the 360 controller architecture has something like this).

Lasse V. Karlsen
A: 

The 360 controller can only report it's current state. There is no other means to get the state without polling. Using System.Threading.Timer or opening up a new Thread that does Thread.Sleep() is really the same in my view, both of them fulfill the functionality of a UI-less timer class.

Thanks for mentioning ISynchronizeInvoke, I've googled some more and found someone that shares my pain and might even have a solution: http://geekswithblogs.net/robp/archive/2008/03/28/why-doesnt-dispatcher-implement-isynchronizeinvoke.aspx

I'll try his code and keep this thread posted... Thanks for the tip!

damageboy
A: 

So... It appears the information / code @ http://geekswithblogs.net/robp/archive/2008/03/28/why-doesnt-dispatcher-implement-isynchronizeinvoke.aspx does indeed provide a working solution. The code is completely independent of any UI, and is instead only dependent on ISynchronizeInvoke for proper UI / Thread integration.

Having just used this, I'm still somewhat reluctant to leave it as is.

The gist of my problem is that every event invocation function looks something like this:

protected virtual void OnLeftThumbStickMove(ThumbStickEventArgs e)
{
  if (LeftThumbStickMove == null) return;

  if (_syncObj == null || !_syncObj.InvokeRequired)
    LeftThumbStickMove(this, e);
  else
    _syncObj.BeginInvoke(LeftThumbStickMove, new object[] { this, e });
}

It's very annoying and confusing to write code this way, it looks, to me, like way too much fuss around just getting the damn thing to work. Basically I don't like having to wrap every event call with so much logic(!)

Therefore, I've opted for a different / additional strategy: Basically, the constructor for the XBox360GamePad class now looks like this:

public XBox360GamePad(UserIndex controllerIndex, Func<int, Action, object> timerSetupAction)
{
  CurrentController = new Controller(controllerIndex);
  _timerState = timerSetupAction(10, UpdateState);
}

As you can see, it accepts a Func that is responsible for creating the timer and hooking it up.

This means that by default, INSIDE the 360 Controller class I don't need to use any UI specific timers... In essence, my "default" constructor for the controller looks like this:

public XBox360GamePad(UserIndex controllerIndex) : 
  this(controllerIndex, (i,f) => new Timer(delegate { f(); }, null, i, i)) {}

I use a lambda function to code the dependency of the controller class on a timer "service".

From WPF, I use the more general constructor to ensure that the control will be using the DispatcherTimer like this:

  _gamePad = new XBox360GamePad(UserIndex.One, (i, f) => {
    var t = new DispatcherTimer(DispatcherPriority.Render) {Interval = new TimeSpan(0, 0, 0, 0, i) };
    t.Tick += delegate { f(); };
    t.Start();
    return t;
  });

This way, I basically leave it up to the "user" to provide the timer implementation for the control, where the default implementation doesn't have to use any WPF or WinForms specific code.

I personally find this more useful / nicer design than using ISynchronizeInvoke.

I would love to hear feedback from people that like this / want to improve this / are disgusted by this etc.

damageboy
A: 

You could also take a look at the Concurrency and Coordination Runtime, which is part of Microsoft Robotics Developers Studio (but will be a stand-alone product soon).

The CCR is the thread coordinator that allows MRDS to fully supports things like joysticks driving robots around, and operates on a message-passing model. You can set up timers that request data from the XBox joystick, which in turn posts data to ports (queues). Then you set up methods that get called (receivers) when data is posted to the joystick port to handle the actions. Adapters for WinForms are provided and it wouldn't be difficult to write ones for WPF.

Here's an awesome article that illustrates how to use the CCR without the full-blown MRDS. http://msdn.microsoft.com/en-us/magazine/cc163556.aspx

In my experience, once you get the basics of the CCR under your belt, doing multithreaded concurrent asynchronous programming actually becomes easy.

MattValerio
A: 

@MattValerio: I know about CCR, but this doesn't really fit the bill here since I plan to put the control on googlecode and the CCR isn't part of the framework or opensource.

damageboy