I’ve been reading up on node.js recently, and have been pretty intrigued with what I found. The long weekend seemed like a good opportunity for some extended hacking. After setting up a nice little development enviroment, and reading up on some other node.js projects, I set myself the following goal for the weekend. Build a web app with a node.js backend that would let me remotely control my media center box.
Now we aren’t talking fully on VNC here, just basically a way to turn my laptop into a remote keyboard and mouse for the other computer. That way I could open and browse hulu without having to get up.
Luckily, I’m happy to say the project was a complete success. If you need proof check out the video below.
Right now it’s real basic. Because of some quirks with jQuery, chrome and the keypress vs. keydown events, all letters are capitals. Also key combinations like Ctrl+C and Ctrl+V don’t work, but it’s passable for most tasks I need it for and gives me a fun base to play around with.
The Secret Sauce
It’s actually not much more than 200 lines of code, not including the libraries. The most complicated part is the communication between my server and the XServer. I started out trying to use Andrey Sidorov’s node-x11 library, which is a pretty neat little project. Basically, it uses the net library in node.js to connect to the Unix X11 Socket and stream requests to the socket over TCP/IP. Unfortunately it’s pretty new, so there really aren’t any methods for sending mouse or keyboard events across the socket yet. I used his framework to write out a new WarpPointer method, which moves the mouse around, but quickly found that sending click or keypress events was going to be waaaaay more complicated.
So for those areas specifically I took a fallback strategy, and resorted to a quick little hack. Instead of talking with the XServer directly, I had node.js kick off a child process and run the command line tool “xdotool.” xdotool is basically a simple command line window management tool that lets you fake XEvents with a fairly simple syntax. Way simpler than building a request for scratch. Of course it’s an ugly hack, but adding the pieces to Sidorov’s framework to send the events to the XServer over the weekend wasn’t going to happen. As the library get’s fleshed out, I’ll strip that hack out.
I had to resort to a similar hack to get the size of the users current resolution, basically parsing the results of an “xwinfo” call. Again, something that I’ll have to eventually refactor out, but good enough for right now.
If you want to see the detailed code, I’ve got it up here on github.
I don’t plan to shelve this hack by any means. In fact I’d love to expand it to a more full featured remote control type app. Right now I’ve got a short list of 4 goals.
1. Improve interaction with X so it’s not calling out to the command line to do the manipulation
2. Improve mobile support. Right now, since moving the browser window around in a mobile doesn’t fire a mousemove() event, you can’t use the remote on a phone.
4. Build out some short cuts/quick links functionality. For example, a hulu browser, that will let you search hulu and start shows with a few keystrokes. Or a short cut tab that will let you quick start often used programs.
So what did I learn about node.js from this first little foray? Well I’m certainly impressed at how easy it was to get something basic up and running. Especially considering that I’d never worked with websockets before, socket.io made it dead simple. The async nature of node.js was also especially interesting to work with, but not too unfamiliar with because of my work with Silverlight.
Of course, I probably could have done the same thing with ruby, or mvc.net, etc, but I don’t think it would be as quick. One of the things I liked about node.js was how easy it was to jump between scripting type stuff, like running console apps and parsing the output, and more traditional server type tasks. It was also great to pass JSON objects across the wire through sockets, and then be able to immediately consume those objects in server side code without having to jump through any hoops to parse those objects into server side variables.