Monday, January 05, 2009

basic JavaScript engine: the go forward strategy after looking back

After 2008's Tokamak in Italy, I blogged about scripting and my cracktastic thoughts on it. I mumbled on about ninjas and myspacers and apparently the drugs are still in my system because I still think it's a solid approach to the problem.

Thankfully I was able to convince a couple others of that as well and the widgets API we put into libplasma, which really just wrap plain QWidgets-in-QGraphicsProxyWidgets thereby taking the boredom out of using them and giving a nice scripting-friendly view on them, was one result. The other was the JavaScript AppletScriptEngine in kdebase.

Rich Moore started the work on these and got a fair ways into it. Then he got busy with his day job. Then he got really busy with his day job. We talked about it on IRC a bit, and I said I'd try to help pick up the slack. Chani and I ended up doing the widget API and Rich got some more work done on the script engine. Then Rich got really, really busy and things stagnated. It happens. (Rich, if you're reading this, we can't wait to see back around. =)

Then Chani decided the other week to write a Plasmoid. Something simple. After months of work on the widgets-on-screensaver insanity project I totally understood her desire for something a little less frustrating, a little more immediate and lot more fun.

However, I begin to think that "Chani" means "trickster" in some devious ancient language because she started doing it in JavaScript. Not with the "ninja" engine in playground (sensible, it's still in playground after all) but with the "myspacer" one that had already trundled into kdebase. That was when we got reminded how incomplete it was.

So Chani, Marco and I jumped on the poor beast and over the span of a few days implemented access to DataEngines, Services, context menu actions, configuration settings, layouts, access images shipped with the Plasmoid in its package and more. The foundation we built on (Qt, QScript, the core KDE libs, libplasma and Rich's initial work in the engine) were all solid and so we raced ahead at great pace and now you can do actually useful things with it.

We wrote a few little test applets to poke at the engine with and as tagging approaches within the next 24 hours for RC1 it's actually moderately useful. =) Take this guy for example:



If we remove the debug output and whitespace, it's 27 lines of JavaScript that can control any music player that exports an MPRIS controller interface (thanks to the nowplaying DataEngine). It all starts with:

engine = dataEngine("nowplaying");
watchingPlayer = engine.sources[0];
controller = service("nowplaying", watchingPlayer);



Yep, it just rips right into things, grabbing DataEngines and Services and flinging them around. In C++ we would've written a couple dozen lines of code (class declaration, constructors, etc, etc) and a CMakeLists.txt to get this far. So while the Plasma, KDE and Qt APIs are great, there's entry overhead we can avoid by using higher level langauges in the right places, no doubt about that.

Next it defines three functions: dataUpdate, stop and setProgress. I won't bore you with those, you can see them yourself over on websvn by looking at the whole file. It then makes a simple UI, all sparkly, performant and themed thanks to libplasma and Qt:

layout = new LinearLayout(plasmoid);
layout.setOrientation(QtVertical);

label = new Label();
layout.addItem(label);

stop = new PushButton();
stop.text = "Stop";
layout.addItem(stop);

progress = new Slider();
progress.orientation = QtHorizontal;
layout.addItem(progress);



It then glues stuff together:


stop.clicked.connect(plasmoid.stop);
progress.sliderMoved.connect(plasmoid.setProgress);

controller.associateWidget(stop, "stop");
controller.associateWidget(progress, "progress");

engine.connectSource(watchingPlayer, plasmoid, 500);


Those controller lines are somewhat magical. They connect the widget to aspects of the Service, so when for instance the music stops the Stop button automatically fades out and becomes disabled. Neat.

The last line connects the DataEngine up and the widget "goes live" at that point. When the music player starts playing, the Stop button becomes enabled, the name of the song, artist and time is printed in the label above and the progress slider starts marching along.

Clicking Stop actually stops the player as you'd expect:

plasmoid.stop = function()
{
controller.startOperationCall(controller.operationDescription("stop"));
}


Moving the slider seeks within the song:

plasmoid.setProgress = function(progress)
{
operation = controller.operationDescription("seek");
operation.seconds = progress;
controller.startOperationCall(operation);
}


All pretty simple stuff, and documentation for the entire API exposed in this simplified window into Plasma is on the way.

Installing the Plasmoid itself is a snap: plasmapkg -i script-nowplaying. In it's final form as a zip file (so `zip -r nowplaying.plasmoid *` in the root dir of the plasmoid), `plasmapkg -i nowplaying.plasmoid` suffices or you can install it via the Add Widgets dialog in the Plasma desktop shell. The Plasmoid above becomes a 1.5K file on disk that you can toss about quite easily, including between operating systems with completely different toolchains and hardware architectures.

We have planned out some tools to help you create Plasmoids extremely easily, so there will be no need to know the packaging details at all for instance. These things will only get better.

I'm really impressed with how quickly it came together over the last few days compared to how actually useful it already is. Writing a high quality language runtime is not an easy task. Writing frameworks like Qt, KDE's core libs, Solid, Phonon, Plasma, etc are also not easy tasks. But gluing the results of those two efforts together? Amazingly quick, if a little black magic-y in places.

Thanks go out to everyone whose worked on those hard bits, and especially Rich Moore, Marco and Chani for helping bring this part of the puzzle together.

Where Do We Go From Here?



If you care about non-C++ languages and Plasma, come join us. We've already got a few Ruboids and Pythonistas hanging about on plasma-devel@kde.org and on #plasma writing Plasmoids, but we need more of you. JavaScripters with an itch to scratch should come join us, too. We need to kick the living crap out of these ScriptEngines and torture them in all sorts of ways, just as we have the C++ libplasma over the last year, and create cool things with them in the process. This way we can go from our first-cut ScriptEngines in 4.2 here to kick ass ones in 4.3.

We're willing, in fact desiring, to improve, modify and grow the ScriptEngines in response to usage. It's the best (some might say only) way, really. =)

I'll be announcing the documentation for the Basic JavaScript ScriptEngine when it's ready to go so you can all jump on it. The docs will come pre-4.2.0, so that you should be able to upgrade to KDE 4.2 and start hacking JavaScript with documentation in hand all in the same day without compiling any C++ on your own. Huzzah.

11 comments:

Dave said...

Promising, I look forward to playing with it. 3 (decreasingly related) questions.

on the subject of slightly alien plasmoids, will the cleanroom apple stuff be in for 4.2?
I tried making a "web widget" based on the assumption that it was of the sort defined by the w3c, but failed to get anything productive to happen. Is this still being worked on?

I like the way things are heading, thanks for all the hard work, to you and the whole team.

Aaron J. Seigo said...

> on the subject of slightly alien plasmoids,

well, our goal is to make non-c++ plasmoids as non-alien as possible, really =) that said ......

> will the cleanroom apple stuff be in
> for 4.2?

what we received is, which is a few more of the classes. someone who knew what they were doing with javascript could probably bang out the rest of what's missing in an evening or two.

here's what we have right now:

http://websvn.kde.org/trunk/KDE/kdebase/workspace/plasma/scriptengines/webkit/dashboard/

> I tried making a "web widget" based
> on the assumption that it was of the
> sort defined by the w3c, but failed
> to get anything productive to happen.
> Is this still being worked on?

i have no idea. to be honest, it really looks like a fair amount of policy wonkery amongst people who all invented their own stuff and realized after the fact maybe it should all work together.

we invented our own system as well, but we also have rather different requirements, though plasmoids are structured rather similar to what others are doing.

the amount of text they've produced on web widgets and the small amount of usefulness it currently represents is pretty depressing, though.

i'd rather be making things in the open, and making other things work with our thing, than policy wonking on something i'm not convinced will ever be successful.

guillaume.debure said...

Don't know much javascript but being able to write plasmoids in languages simpler than C++ is great !!! I might try to write some that would be useful to some people around me, "kwin plasmoids". Let me explain : KWin is really great, but most of its magic for the end user is hidden behind keyboard shortcuts (ctrl+F8, ctrl+F9, meta+ctrl+F12...) that might be hard to remember for not-so-techy people. You can always use screen edges and corner, but this tends to get in the way of these same people.

My idea would be to create one plasmoid per kwin effect, embed them in a container (a la "system monitor" plasmoid"), and let the user put it (for example) in a auto-hide panel on top of the screen. He would then see the panel containing one icon per activated kwin effect.

No idea how difficult this would be, but I really would like to give it a try

Thanks for your always excellent information, keep up the terrific work !!!

Einar said...

As one of the "pythonistas" who recently started working on Plsma I can say that the API is quite nice. Once the helpful people in pnel-devel answered my questions, I wrapped a custom and almost fully featured DataEngine in Python in just 40 LoC, *including* whitespace.

Dave said...

Good documentation is enough to get me to scratch whatever particular itches. (Free)pascal doesn't talk to much of anything in the Qt stack in any particularly good way (it isn't written in C so doesn't natively support c++'s flavors of name clobbering), so I'll have to learn/refresh knowledge as it is. It's a bit too daunting to reverse engineer in a non-core language and expect good results.

:D

Frando said...

Amazing.
Is there a simple method to issue a dbus call from within a javascript plasmoid? That would be very useful.. to have access to all other parts of KDE (or, at least the one we can talk to via dbus).
Also, is it possible to issue simple shell commands and then parse out the results?

Aaron J. Seigo said...

@Frando: no dbus support yet; there is an executable DataEngine that handles executable calls.

however, we'll be locking that one down since it's a bit dangerous ;) to expose to random widgets streaming in over the net.

Chani said...

the screensaver thing was insane, yes, but it was fun too. ;)

@guillaume.debure: I like that idea! :) add a tooltip with the keyboard shortcut too, so that eventually it can be remembered. :) assuming kwin has a good dbus interface, it should be trivial to make an icon that triggers an effect when clicked.

guillaume.debure said...

@Chani : thanks !!! I *do* hope it is trivial, since my coding ability is quite limited (but slowly growing) !!! What is the best mailing list for assistance on this : plasma, kwin, or kde ?

Aaron J. Seigo said...

@guillaume.debure: we can help you in #plasma on irc and plasma-devel at kde dot org by email =)

Frando said...

"assuming kwin has a good dbus interface,"
didn't aseigo just state that there's no dbus support yet?
It would be really really great if a simple method to issue a dbus call could still be added for 4.2. This would make many many great things possible for simple javascript plasmoids.