Over the summer of 2014 I decided to enhance our home theatre PC’s with a launcher that simplifies the use of media player apps and web content, while saving time and energy.
The Problem
The world of home entertainment is changing rapidly, especially with the advent of connected devices like the Apple TV, Roku and other ‘smart TV’ solutions combined with the ever-changing world of cloud services.
However, for now, the best all-round solution for home entertainment remains to be home theatre PC’s. At home we’ve got two HTPC’s and a server. I’ll get to the server another day, but for us the best solution is a combination of three programs:
-
Plex, for playback of local media collections
-
MediaPortal, for playback of recorded broadcast TV
-
Kodi, for streaming
Kodi is actually capable of doing it all, using the PleXBMC and MediaPortal addon, but you really can’t beat the native experience of each app.
Without a mouse and keyboard, it’s hard to switch between all these apps. Also, waiting for each one to start up every time becomes frustrating.
This led to the following requirements:
- Ability to switch between applications using a remote
- Switching between applications should be fast
- Applications should be able to be added easily
- A user interface that be used for other helpful information
Development Process
The project was split into two parts. The frontend is all about giving a responsive and clear interface for the user, while the backend manages the applications and sends commands to the operating system.
Frontend
I started out with a simple Window’s Forms interface made up of a few circles. I quickly found that Windows Forms are limited in the visual effects they offer and aren’t very easy to work with.
The solution was to embed a web interface into the app. Why a web interface? You can make just about anything in CSS, the interface can be animated and used visual effects like shadows and rounding, and there is plenty of resources and frameworks available to make the job easier.
Also, because the web interface sits on the home server it is very easy to update the available applications rather than recompiling and redistributing.
Tools of the Trade
Over summer I was working on another project in AngularJS so I decided I’d build this in Angular too. Angular makes it easy to ensure the interface is always up to date with the model.
I also used Yeoman to setup all the basics, such as the Bower package manager, and Grunt for task running.
Using Bootstrap allowed me to get started easily, as well as make the frontend responsive so it would work well for different resolution TV’s.
Design
I’m no interaction design expert, but with enough iterations the interface got to a point where it’s both easy to use and flexible.
The initial layout looked pretty nice when it was built, however I got to the stage where I wanted to be able to add more applications and have the interface more responsive to different screen sizes.
I decided to switch to an Apple TV inspired grid layout. This was easily responsive and could hold as many applications as I needed by scrolling the page when it got to the bottom.
Embedding the user interface
Windows Form’s has a built in web browser but it’s based on Internet Explorer. Enough said, it was time to embed Chrome. CEF is an embeddable version of Chrome, and CefSharp allows CEF to be embedded within a C# application!
This also allows me to call JavaScript functions from C#, and call C# methods from JavaScript, which is much easier than communicating through sockets or HTTP calls.
Backend
The backend’s job is to manage applications based on commands from the frontend.
Language of Choice
Our HTPC’s both run Windows 7 and I’ve used C# before, so this was my language of choice.
Design Patterns
Having just completed an object oriented programming paper at university I was keen to put this to use with some design patterns.
Without these design patterns, my source code would become a mess and any future changes would become painful.
A Facade is used to handle interactions between other classes, acting as the one class every other class can talk through. For example, if the FrontendBridge gets a message from the frontend to open an application, the facade finds the application and sends it the open message.
State ensures that the state of an application doesn’t change the way we interact with it. Other classes have idea what state an application is in, they just send it commands. This makes implementing new states easy, and saves plenty of ‘if’ statements.
This is used by storing an ApplicationInstance in LaunchableApplication, and a method to change the current application instance (for state transitions).
Singleton’s are very necessary for implementing a Facade or the FrontendBridge (things there are only ever going to be one of). A singleton is a static method that initialises an instance of a class or just returns the single instance of the controller if an instance already exists.
Suspending Functionality
One of the most important aspects of this project is to be able to switch between applications quickly. The computers I was using aren’t particularly fast, so waiting 20 seconds for an application to launch gets annoying!
One option is to just keep all applications open which would work fine on some computers, but this would have a nasty effect on these machines. These computers are also left on all day and night, so reducing the amount of CPU will be better for my parents power bill!
The best solution for me was a command line utility called PsSuspend. Use of this is very simple, just launch PsSuspend.exe with the process ID to suspend, and add the -r
flag to resume.
PsSuspend completely freezes an application so that it uses 0% CPU, but it remains completely open in memory.
Resuming a process brings it back to life instantly. I wired up the PsSuspend command up to to the close()
method of my OpenApplication state (switches to the suspended state), and to resume when the SuspendedApplication state receives a launch()
.
Application Switching
Moving an application (or the launcher) to the front of the screen turned out to be harder than I expected.
For the longest time I was having an issue where an application could be launched fine, but would not return to the launcher if the application had received any keypresses. Even weirder was this only happened on the HTPC’s but not in my development environment. I even asked about the issue I was having on Stack Overflow, but gladly found the answer myself in the end.
The difference was that I was running with debugging while the other machines weren’t. The breakthrough came from reading a tutorial on CodeProject which explains the rules for getting focus from another window. If an application is being debugged then it is allowed to switch back to itself. The solution was in a blog article which avoided the limitations by programmatically pressing the ‘alt’ key - the article has since been deleted, but you can see the code I used to solve this issue on GitHub.
Catching remote button presses
I had trouble catching keyboard events while the launcher was not active, plus I needed support for a specific button on the remote. As a result, the easiest way was to setup EventGhost to make a UDP request to the launcher.
Using EventGhost gives good support for most remotes, and the fact that it’s UDP means a smartphone or other device could also trigger the launcher if needed.
Extensions and Further Development
Osprey Launcher continues to be used by my parents. Eventually I intend to prepare it for open source release, as well as adding a few extra features.
Launching Websites
It’s pretty neat to see how new ideas evolve as software is used, and this feature is a great example.
Originally, CEFSharp was just used to display the frontend. However, with a little modification websites were treated as launchable items, giving many extra uses for the launcher.
Desktop Mode
Something I really like about the launcher is that it simplifies the whole experience of using the HTPC by hiding out the complications of the operating system behind it.
The Desktop Mode adds to this experience, by treating the operating system below like an application. Apps open in the launcher are completely hidden, even from the Windows taskbar making for a pure Window’s experience while in desktop mode.
Force Close
Things don’t always work perfectly, especially on these slow old computers. So task manager doesn’t need to be launched, I added a ‘Force Close’ option in the context menu of a launchable application.
Automatically return to launcher
At most, the computer would only be used a few hours a day. If an application is left open, this can keep the fan on all day until the computer is used next.
To keep CPU cycles low, the launcher will pull out of an application after a few hours.
Frontend Widgets
The launcher provides a nice way to integrate the HTPC’s with information from the server. I made a few AngularJS directives to provide this information.
- Number of recordings on MediaPortal today
The MediaPortal install on the home server has MPExtended installed, providing an web API into the MediaPortal database.
Counting the number of results returned by TVAccessService’s GetScheduledRecordingsForToday did the trick.
- Hard drive space available on server
This displays the available space percentage shown from a PHP script on the server. This one is handy with a home server setup like this, as the server can easily go months without anyone logging into it.
Take Away
As well as creating something neat, this project has been a major learning experience, giving me some insights:
- Spending time to think about the best class structure is worth it in the end
- User input provides new ideas and changes, so get it into the hands of users as soon as possible
- Don’t re-invent the wheel, there are plenty of frameworks and tools available to save time
- The Windows API is a mysterious beast
The code
If you’d like to have a look at the Osprey Launcher source code, check it out on GitHub. Note that that as of 2023, I no longer maintain this repo and it may no longer work - but you’re welcome to try get it working or use it as inspiration for your own projects.
Edit, 15 July 2023 Edited to add a link to the Osprey Launcher source code.
Edit, 30 Aug 2023: Edited to remove the link to the (now deleted) blog article which helped me with the window switching.