Making your Maemo 5 Widget Configurable

I wanted to add the ability to configure a widget I was working on. Low and behold the sample code from Maemo.org doesn’t tell you how to do this. I downloaded the source for a widget I use which I know has a settings dialog. Turns out you need to do the following within **_plugin_init():


hd_home_plugin_item_set_settings (&self->parent, TRUE);
g_signal_connect (&self->parent, "show-settings", G_CALLBACK (BUILD_CONFIG_UI_CALLBACK), self);

and inside your build config put up a dialog


GtkWidget *dialog = gtk_dialog_new_with_buttons ("Personal Dataplan Monitor Settings",

Not so bad, right? Now if only it were included in the tutorials…

GLib DBus vs Python DBus

I’ve been experimenting with DBus a bit lately for some side projects im doing (more on that soon). Part of the project involves spawning a service via dbus. In python this is trivial and easily discoverable with a google query or two:


import dbus
bus = dbus.SessionBus()
bus.start_service_by_name('org.gnome.Rygel1') # org.gnome.Rygel1 = service name

Ridiculously easy, right? In C using the glib dbus library things are much more difficult. A bit of googling yielded almost nothing when searching for “start service by name c glib” or variants thereof. So I did the evil thing, I checked out the python source. start_service_by_name is turns out, is a wrapper to a normal proxy call to the org.freedesktop.dbus interface function StartServiceByName:


return (True, self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
BUS_DAEMON_IFACE,
'StartServiceByName',
'su', (bus_name, flags)))

Alright… so now how do we do this in C? We could fiddle with dbus_g_proxy_call for a while, or find out that Maemo actually gives us the answer.


DBusGConnection *connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);

DBusGProxy *proxy = dbus_g_proxy_new_for_name (connection,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS);
int result;
dbus_g_proxy_call (proxy, "StartServiceByName", &error,
G_TYPE_STRING,
"SERVICE_NAME",
G_TYPE_UINT, 0,
G_TYPE_INVALID,
G_TYPE_UINT, &result,
G_TYPE_INVALID))

Simple, right?

Porting GLib Applications to Python

So part of a project I am doing for my university involves writing python bindings for a library called GUPnP. GUPnP is a UPnP framework written by Zeeshan Ali. It is written using GLib and GObject and uses the GLib based Libsoup http backend.

On day one Zeeshan pointed me to GObject Introspection (GIR). GIR is this really awesome peace of work that is able to scan c code and generate what is essentially a compressed api/marshalling definitition file. There are then libraries written for several languages which understand these files (called ‘typelibs’) and are capable of marshalling data back and fourth between languages. Until recently the only option for doing this translation was a project called PyBank. PyBank has recently been merged with PyGObject which provides GObject support and also GIR support (thanks Simon [svdlinden]!).

So, I wrote my proposal for school and got sponsorship to visit the Maemo Summit. There I met Zeeshan Ali (great guy, btw!) and also got a free Nokia N900. When I finally got over hacking the N900 with widgets galore (stay tuned for another post about that…) I sat down and started working on GUPnP-Python via GIR.

So to build a GIR typelib there are two applications to help you along. The first is g-ir-scanner. The following command (only half complete — those hardcoded paths need to be pkg-config-erized) was able to generate bindings for GUPnP. Notice that it is required that all of GUPnP’s dependencies already have GIR support (and hence typelibs) installed in the proper locations before GUPnP would scan and compile. I had to add GIR to both GSSDP and libsoup before GUPnP would get bindings. (Patches to those libs imminent…) Also note the explicit addition of -I and –include for all of the includes. (-I required to properly scan the file for symbols, and –include to add the include to the intermediate .gir file so the eventual .typelib can find external symbols).

Modifications to Makefile.am needed to get the demo working (does not include all headers yet):

SCANNER_BIN = g-ir-scannerSCANNER_ARGS = -v --add-include-path=.

GLIB_INCLUDEDIR=`pkg-config --variable=includedir glib-2.0`/glib-2.0GLIB_LIBDIR=`pkg-config --variable=libdir glib-2.0`GLIB_LIBRARY=glib-2.0

GUPnP-1.0.gir:        env PYTHONPATH=.:.. env LPATH=.libs 577331 577808 577331SCANNER_BIN) 577331 577808 577331SCANNER_ARGS)             --namespace GUPnP --nsversion=1.0             --noclosure             --output              --strip-prefix=            --libtool="577331 577808 577331LIBTOOL)"             --library=gupnp-1.0             -I/usr/include/libsoup-2.4             -I/usr/local/include/gssdp-1.0             -I/usr/include/libxml2             -I/usr/local/include/gobject-introspection-1.0             -I577331 577808 577331GLIB_INCLUDEDIR)             -I577331 577808 577331GLIB_LIBDIR)/glib-2.0/include             --include=GObject-2.0             --include=GSSDP-1.0             --include=libsoup-2.4             --include=libxml2-2.0             --pkg gupnp-1.0             gupnp-control-point.h gupnp-context.h gupnp-resource-factory.h             gupnp-device-proxy.h gupnp-service-proxy.h gupnp-device-info.h             gupnp-service-info.h gupnp-service-introspection.h

GUPnP-1.0.typelib: GUPnP-1.0.gir        g-ir-compiler --includedir=. 577331 577808 577331G_IR_COMPILER_OPTS)  -o  GUPnP-1.0.gir

Once we finally had GUPnP-1.0.gir (the actual scanned API in an XML format) and the .typelib (essentially a compiled and compressed version of the .gir) generated and put in place it was time to fire up pygobject!

>>> from gi.repository import GUPnP>>> print GUPnP

Success! Now to try and actually write a meaningful app. I felt the best way to do this was to first get a C based application using GUPnP and re-write it in python. So I grabbed some sample code from the GUPnP website and modified it a bit.

#include #include "libgupnp/gupnp.h"

static GMainLoop *main_loop;

static voiddevice_available_cb (GUPnPControlPoint *cp,                     GUPnPDeviceProxy *proxy){  

GUPnPDeviceInfo* gupnp_device_info = GUPNP_DEVICE_INFO(proxy);  g_print("Device model name: %s", gupnp_device_info_get_model_name(gupnp_device_info));

}

int main(int argc, char** argv){ 

 GUPnPContext *context;
 GUPnPControlPoint *cp;

  /* Required initialisation */  
g_thread_init (NULL);  g_type_init ();

  /* Create a new GUPnP Context.  By here we are using the default GLib main     context, and connecting to the current machine's default IP on an     automatically generated port. */  
context = gupnp_context_new (NULL, NULL, 0, NULL);

  /* Create a Control Point targeting WAN IP Connection services */  
cp = gupnp_control_point_new    (context, "upnp:rootdevice");

  /* The service-proxy-available signal is emitted when any services which match     our target are found, so connect to it */   
 g_signal_connect (cp,                    "device-proxy-available",                    G_CALLBACK (device_available_cb),                    NULL);

  /* Tell the Control Point to start searching */  gssdp_resource_browser_set_active (GSSDP_RESOURCE_BROWSER (cp), TRUE);

  /* Enter the main loop. This will start the search and result in callbacks to     service_proxy_available_cb. */  main_loop = g_main_loop_new (NULL, FALSE);

  g_main_loop_run (main_loop);

  /* Clean up */ 
g_main_loop_unref (main_loop);  
g_object_unref (cp);  
g_object_unref (context);

  return 0;
}

And compiled..

gcc -I/usr/local/include/gupnp-1.0 -I/usr/local/include/gssdp-1.0 -I/usr/include/libxml2 -I/usr/include/libsoup-2.4 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/gtk-2.0 -I/usr/lib/gtk-2.0/include -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/pixman-1 -I/usr/include/freetype2 -I/usr/include/libpng12 -pthread -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I../.. -DDATA_DIR="/usr/local/share/gupnp-tools" -g -O2 -Wall -o test -pthread -Wl,--export-dynamic  -L/usr/local/lib /usr/local/lib/libgupnp-1.0.so -L/usr/lib -luuid /usr/local/lib/libgssdp-1.0.so /usr/lib/libsoup-2.4.so /usr/lib/libxml2.so /usr/lib/libgnutls.so /usr/lib/libtasn1.so /usr/lib/libgcrypt.so /usr/lib/libgpg-error.so /usr/lib/libgtk-x11-2.0.so /usr/lib/libgdk-x11-2.0.so /usr/lib/libatk-1.0.so /usr/lib/libgdk_pixbuf-2.0.so /usr/lib/libgio-2.0.so /usr/lib/libpangocairo-1.0.so /usr/lib/libpangoft2-1.0.so /usr/lib/libcairo.so /usr/lib/libpixman-1.so /usr/lib/libglitz-glx.so /usr/lib/libGL.so /usr/lib/libXext.so /usr/lib/libglitz.so /usr/lib/libpng12.so /usr/lib/libXrender.so /usr/lib/libX11.so /usr/lib/libXau.so /usr/lib/libXdmcp.so /usr/lib/libpango-1.0.so -lm /usr/lib/libfontconfig.so /usr/lib/libfreetype.so -lz /usr/lib/libexpat.so /usr/lib/libgobject-2.0.so /usr/lib/libgmodule-2.0.so -ldl /usr/lib/libgthread-2.0.so -lpthread -lrt /usr/lib/libglib-2.0.so  -pthread test1.c0
./test1
Device model name: MediaTomb

Success! It found my local media serve and correctly printed the model name.

I decided to start small with the python application. Lets first just make a UPnPContext

from gi.repository import GLib, GUPnP

# Get a default maincontextmain_ctx = GLib.main_context_default()

# Bind to eth0 in the maincontext on any port
ctx = GUPnP.UPnPContext(main_ctx, "eth0", 0)

This, however, failed miserably:

** (process:20844): WARNING **: (pygi-argument.c:1818):_pygi_argument_release: runtime check failed: (!is_pointer || transfer == GI_TRANSFER_NOTHING)

GLib-ERROR **: The thread system is not yet initialized.aborting...Aborted

(That warning will persist even when we eventually get it working. I plan to file a bug). Well, the error it gives is pretty clear, ‘thread system not initialized’. Alright, lets initialize the thread system:

from gi.repository import GLib, GUPnP

GLib.thread_init(None)

# Get a default maincontext
main_ctx = GLib.main_context_default()

# Bind to eth0 in the maincontext on any port
ctx = GUPnP.UPnPContext(main_ctx, "eth0", 0)

---

Traceback (most recent call last):  
File "bad.py", line 3, in     GLib.thread_init(None)  File "gi/types.py", line 34, in function    return info.invoke(*args)RuntimeError: Could not locate g_thread_init: `g_thread_init': /usr/local/lib/libglib-2.0.so.0: undefined symbol: g_thread_init

Well, thats even worse. A quick strace and ldd confirmed that libglib-2.0 does indeed not contain the symbol g_thread_init. Whats funny is that normally pygobject doesn’t allow you to call functions that doesn’t exist in C through GIR. It complains that the function doesn’t exist at all — not that its not in the shared object! I stopped into #pybank on IRC and had a lengthy discussion with user malept who pointed out that the symbol is in fact in libgthreads, and that I was probably the only person using libgthreads + pygobject + GIR. Ah!

It took a couple hours of headbanging but eventually I discovered that GObject also can initialize threads…

from gi.repository import GLib, GUPnP, GObject

GObject.threads_init()

# Get a default maincontextmain_ctx = GLib.main_context_default()

# Bind to eth0 in the maincontext on any port
ctx = GUPnP.UPnPContext(main_ctx, "eth0", 0)

That worked, the program exited normally. At this point coding the rest of the app using the C code as a model was fairly simple (a bunch of guess and check on how the C -> python-ization at times worked out well, for example in C its gssdp_resource_browser_set_active() and in python its GSSDP.ResourceBrowser.set_active()). Compare the following (working) python code to the original C. Which would you rather write? 🙂

from gi.repository import GLib, GUPnP, GSSDP, GObject

def device_available(cp, device):  
   print "GOT A DEVICE!  Its model name is :", device.get_model_name()

# Note: glib.thread_init() doesn't work here, have to use the gobject callGObject.threads_init()

# Get a default maincontext
main_ctx = GLib.main_context_default() 

# Bind to eth0 in the maincontext on any port
ctx = GUPnP.UPnPContext(main_ctx, "eth0", 0)

# Pretend to be a root device (Zeeshan needs to document these uri things...)
cp  = GUPnP.UPnPControlPoint(ctx, "upnp:rootdevice")

# Use glib style .connect() as a callback on the controlpoint to listen for new devices
cp.connect("device-proxy-available", device_available)

# "Tell the Control Point to Start Searching"

GSSDP.ResourceBrowser.set_active(cp, True)

# Enter the main loop which begins the work and facilitates callbacks

GObject.MainLoop().run()

# Be cheeky, and intentionally NOT clean up memory because python is awesome

print "You'll never know I am here!"

--

 test.py 

** (process:21327): WARNING **: (pygi-argument.c:1818):_pygi_argument_release: runtime check failed: (!is_pointer || transfer == GI_TRANSFER_NOTHING)

GOT A DEVICE!  Its model name is : MediaTomb

Ah, victory. The plan now is to finish the GUPnP bindings (include the rest of the headers), clean up the patches to GSSDP, Libsoup and GUPnP and get them upstream. Then I’ll write a few more demo apps to ensure the bindings work properly (and report/fix bugs) and then on with the rest of the project (and more N900 hacking of course!)

Back from the Maemo Summit 2009 with a N900 in my pocket!

I just got back from the Maemo Summit 2009. First impressions: WOW! The most impactful part of the summit is of course the fact that Nokia gave out nearly 300 brand new, fresh from the factory, N900 devices. I’ve been using the device now for several days non-stop. Ordinarily I would say I am an extraordinarily picky person when it comes to devices, but the N900 gets no major gripes from me. Sure there are a couple gaps here and there, but overall: A+. A longer review may be coming soon. for now, some short reasons:

  • Rock-solid stability. 4 days of hammering and not a single lock up or crash
  • It’s got a HUGE range of functionality, including a browser powerful enough for Hulu, Youtube and *desktop* version of gmail
  • Open Source? Check
  • Keyboard? Much improved over the N97, definetly bordering on ‘good’ but not ‘great’.
  • Linux? Check. Hackable? Check. Easy to code for? Check. Hell I was able to write a homescreen widget in python ON THE DEVICE ITSELF!. Thats right, I created a new widget using just the device’s xterm and vi editor as tools. (More on this in a future post)

But thats not all that important. The N97 could do (almost) everything the N900 can, So why am I jumping off the wall excited? Because the N900 succeeded marvelously where the N97 fell on its fact: Its FUN to use!

The UI is gorgeous; it never lags; the composited transitions are phenominal and the multi-tasking is better than my netbook!

Back this all up with a very engaging open-source community and friendly nokia developers and you really get a whole experience that is years ahead of the competition. (I’ll mention why I think Maemo in the long run is a huge leap ahead of Android/Pre despite their linux backends in a future post).

Anyway, back to the conference. Overall organization and execution were flawless, things went off on time and comfortably. There were always snacks and drinks around as well. The talks themselves varied immesnely in quality as is usual for a conference like this. On the whole they were highly informative and if nothing else served as a huge motivator for me to develop on Maemo going forward.

P.S. : I am using Bluetooth Dial Up Networking (DUN) right now to write this post. This feature has been lamented for not working
out of the box on the N900. I spoke to some developers who assured me this was not because Nokia didn’t want to add this feature, there was simply not enough time to build it into the UI (perhaps itll be native in the future?)

There are instructions for making this work online which (when run as root) work like a charm! Cheers!

P.P.S. 1) A huge thank you to Dr. Jon Smith at the University of Pennsylvania for sponsoring my trip. 2) A hello to Raif Blanford and Zeeshan Ali — two new friends from the conference!

Conferences abound!

I will be attending the Maemo Summit 2009 in Amsterdam this year, with a particular interest in talking to developers about uPnP and DLNA compliance and development. I will be doing a project with upnp on Maemo platform so this is a particularly exciting event for me. I’m also hoping to find a way to get a free Nokia N900 out of this deal :)!

I will also be attending Wineconf 2009 which happens to also be in Amsterdam about a month later.

A huge thank you to Dr. Johnathan Smith at the University of Pennsylvania for sponsoring my trip to the Maemo Summit and to the Wine Party Fund and CodeWeavers for sponsoring my trip to Wineconf.

Where did the last 4 month’s blog posts go?

My web host has seen fit to royally screw me over. See Adam’s take and below, my version of the story.


Dear Sirs/Madams,

Good afternoon! My name is Zachary Goldberg and I have been a
Powweb customer for more than three years. It saddens me today to
write to you to say that after more than three years a situation has
arisen which is causing me great distress. I would like to explain my
current problem and implore you to help me. My main problem is that
all of the data on my powweb account has been
deleted!

Recently I was contacted by your legal department about "files in
violation of policy". I immediately responded to that email from your
legal team asking the most obvious question "Well, I have a lot of
files, which ones are in violation?". After two or three
correspondences
via your ticket system I learned that "files beginning with zi*
outside your htdocs folder" were in violation. Well, I had put no
such files there but sure enough there were more than 4,000 files with
seemingly random names beginning with zi outside my htdocs folder. I
promptly logged in with ftp and deleted all of those files. I assumed
that by doing this everything would be alright.

Three days later my friend and co-owner of the account (Adam Shefki,
CC'ed on this email) called me saying "Whats up, the website is down?
Also, can you log into the FTP?". I checked the ftp, I could log in,
but all of the files were missing!

And so, two days later and after many calls to your support department
(mostly with the "Support Supervisor" named Lester) I have received no
reason for why the files were deleted. I spent more than two hours on
the phone with Lester yesterday (Friday, 5/15). By the end of the day
though, Lester had promised the issue was being resolved and our files
would be back by the end of the day. Today I find the following two
response on my open support tickets:

Hello,

I am writing to you to follow up on the Support Ticket #6004981 . I
had requested you to get back to us with more information on your
issue, but there has been no response from you. I'd like to continue
to assist you with your issue, but unfortunately cannot do so, without
further information. At this point in time, I'm going to resolve your
Support Ticket, temporarily. If you'd like to reopen your ticket, you
can do so from your Support Console at any time.

Regards!

Archie Scott
Customer Support

and

Dear Sir/Madam,
Thank you for contacting support.

I regret, it will not be possible to restore the files at this point,
until our business department has looked into this issue. There is
currently an open ticket to them, but the department is closed over
the weekend, so I am not able to assist further at this point. I
apologize for the inconvenience that this may cause.

Thank You,
Jim M.
Technical Support


The first email says they tried to ask for information. I have
received no phone calls or emails or any other communication from
Powweb since I hung up with Lester yesterday. The second email
confuses me even further, Lester had already assured me the issue
would be taken care of! Also, I am very confused as to why the
business department needs to be involved.

I called your support department this evening who informed me that
there is an additional comment in the ticket system which says
something about a "Disk Space Violation" of which I have heard nothing
about!

Please, I implore you to empathize with me. The websites I administer
have been down for more than two days and I am extremely concerned
about being able to recover the data on the servers! Some of the data
would be excruciating
or even impossible to recover. I am not a spammer or a seller of
anything in appropriate, I maintain a simple personal blog for myself
and a hobbyist blog for my friend Adam.
- Hide quoted text -

Please, is there any way you can help us?

Sincerely,
A customer begging for some help,
Zachary Goldberg

Fundamental Change in America, Trust

Those of you who know me well know Lawrence Lessig is one of my personal heroes. As such I of course check his blog on an hourly basis and eagerly consume everything he puts there.

Today he blogged a, i’ll call it version 2, of his “How we can fix America” story. I’ve heard all of the arguments he has made here before but every time they get more concise and more convincing. If you have never heard him talk or you have even an inkling of suspicion that American government needs improvement over its current form I highly suggest you stop by and listen. I know its long, but definitely informative and worthwhile.

Dear America

Alright, time for some hard truth. We’re in trouble, big trouble. The economy, failing auto industries, our falling position as a world economic power (It’s estimated China’s GDP will surpass the USA by 2015), falling favor in international political circles (Who throws a shoe?).

Uncle Sam has already assigned Billion of the “Bank Bailout” (Which they may not be keeping very close tabs on). However, the auto bailout seems unlikely to happen as of now. I have no qualms with this, the US auto industry put themselves into this predicament and rightfully needs a good bankruptcy to reorganize and figure out how to make a good product.

So while I am comfortable with telling the big 3 to reorganize themselves, I think we have a bigger problem coming up. The US doesn’t make stuff anymore, a fundamental problem if we want to claim ‘energy independence’ or any form of independence for that matter. Especially when it comes to batteries. So it seems we haven’t yet decided to build a big battery plant ourselves. Normally I would agree that the market should be responsible for making a move here but we’ve seen what the US markets tend to do as far as making stuff ourselves. It’s time we got somebody to actually decide ‘Yes, I want to build here, in the US of A, something that will fuel American energy independence.’ And if that means more government (“socialist”) money to kick start it, well for once we would be putting money into our future as opposed to using it as band aides for our past failures.

Speaking of electricity, GM’s Volt seems like lots of smoke and mirrors right now. Today they announced they are shutting down the plant which was set to make the Volt’s engine. Yet somehow they still plan to produce the car. Call me skeptical but a car without an engine, even if its an electrical engine, will not outsell the competition.