19 January, 2012

How great would it be if...

I have been constantly thinking about something in the last months. Unconsciously, it all started when I did my first contribution to GNOME, then I got addicted, and more and more contributions came, and together with that, more research, reference manuals I had to read, APIs I had to use, the GOPW (GNOME Outreach Program for Women)... and then there it was, a NEED!

How many of you did not feel that documentation was incomplete or non understandable at some point and were desperately looking for an example or source of inspiration?? In my case, I usually look in some project under git.gnome.org, and it helps in most of the cases. What about if we had some kind of 'Google Code Search' tool that would search in all the available code at git.gnome.org? Something like a 'GNOME Code Search'! How great would that be??

What do you guys think? Which approach would you take for doing something like this?

17 January, 2012

Add 'Share' functionality to Cheese - check!

Great news! The sharing functionality I have been lately working on, have been merged into Cheese master! Now, users will be able to share pictures and videos within different technologies! This needs some improvement though.

First of all, nautilus-sendto needs a good check in the configure.ac file. We are talking here about a runtime dependency, and it would be really nice to have a hint for the user in the form of a message at configure time if nautilus-sendto is not installed on their system or they have the wrong version of it. I already tried something, but it is not completely working, and therefore had to be dropped from the patch for the merge:

# Check for nautilus-sendto runtime dependency.
NAUTILUS_SENDTO_DEP_MSG="This is a runtime dependency just needed for using the sharing functionality in Cheese."
NAUTILUS_SENDTO_REQUIRED=2.91.0
AC_PATH_PROGS([NAUTILUS_SENDTO], [nautilus-sendto], [notfound])
AS_IF([test "x$NAUTILUS_SENDTO" = "xnotfound"],
   [AC_MSG_WARN([Unable to find nautilus-sendto >= $NAUTILUS_SENDTO_REQUIRED in the path. $NAUTILUS_SENDTO_DEP_MSG])],
   [NAUTILUS_SENDTO_SYSTEM=`nautilus-sendto --version | $SED 's/nautilus-sendto //g'`
    AS_VERSION_COMPARE([$NAUTILUS_SENDTO_SYSTEM], [$NAUTILUS_SENDTO_REQUIRED],
         [NS_VERSION_COMPARE=0],
         [NS_VERSION_COMPARE=1],
         [NS_VERSION_COMPARE=1])
    AS_IF([test $NS_VERSION_COMPARE = 0],
       [AC_MSG_WARN([Unable to check the version of nautilus-sendto, it is probably too old. At least $NAUTILUS_SENDTO_REQUIRED is required. $NAUTILUS_SENDTO_DEP_MSG])])]
)

The problem with this solution, is that it will only work for those versions of nautilus-sendto that include support for the --version argument, currently just nautilus-sendto master, since this is a feature I also worked on during the nautilus-sendto integration, and was merged about a week ago. This means, that old versions of nautilus-sendto will not recognize this argument, and therefore return an error message ("Could not parse command-line options: Unknown option --version"), that AS_VERSION_COMPARE will use, comparing a version number with a string, instead of a version number with another version number, which will of course make the check useless. In the meantime, while I work on finding an improved solution, the user will be informed in two ways in case they hit this problem: through the UI by an insensitive 'Share' action and through the README file.

There are other bits that also need some improvement. As I already explained a few blog posts ago, for accomplishing this task,  I used nautilus-sendto, that internally uses libsocialweb, which needs to have some libsocialweb accounts first set up for working. Right now, the only way for setting those accounts up is using Bisho, that is also giving some problems. The idea now and next challenge, is to use GOA (GNOME Online Accounts) in libsocialweb instead, at least, and just for the moment, for the Google services, so my next tasks will be to focus on porting the current libsocialweb YouTube service to use GOA, and to fix all of those underlying bits that are not working correctly, to make the sharing functionality work really well!

As you can see, I do not get bored. I will be back soon with more news!

03 January, 2012

Avoid using busy-waiting or spinning technique

Busy-waiting, or in other words, spinning, happens when a process repeatedly executes a loop of code to see if a condition is true, while waiting for an event to occur. This means, that the processor will be waiting for the task to be finished in order to go on executing other tasks, which means, that in general this technique should be avoided, since it consumes time of the processor that could be used, wasting it on useless activity. The solution would be to use an asynchronous approach.

Thinking in asynchronous terms is hard though, synchronous API is simpler in many ways, so most of us tend to use it. It is also usually a quicker hack, but not because of this reason it is the best option.

In order to let Cheese users launch several nautilus-sendto dialogs, to send several pictures each time, and change the form of the cursor from when Cheese was busy with nautilus-sendto to when it was not and all of these without blocking the UI, I needed to take an asynchronous approach. I do not have experience with it, so I of course did it the "easy way", spinning the loop (spin_event_loop()) and this was the result (please, take into account that I removed all the code that was not necessary for this explanation):
public void files_share() {
    try {
       main_window.set_busy_cursor();
       Pid child_pid;
       Process.spawn_async("/", argv, null SpawnFlags.SEARCH_PATH, null, out child_pid);
       main_window.set_normal_cursor();
    }
public void set_busy_cursor() {
   get_window().set_cursor(new Gdk.Cursor(Gdk.CursorType.WATCH));
   spin_event_loop();
}
public void set_normal_cursor() {
   get_window().set_cursor(new Gdk.Cursor(Gdk.CursorType.LEFT_PTR));
   spin_event_loop();
}
public bool spin_event_loop() { 

   while (Gtk.events_pending()) // Busy-waiting
     {...}
} 
After submitting my first patch with this code, Dave was the one who made the review, and he suggested me to not use the spinning technique. Now it is obvious after reading the above mentioned reasons why :P. At first I was a little bit "afraid" of using asynchronous API. It looked completely akward to me, but then I also discovered through him, that GIO and GLib have some nice API to make asynchronous operations simpler. GIO offers GSimpleAsyncResult, which I personally did not find specially pretty, but still very useful. Sushi uses it and has some nice examples in its code. On the other hand, GLib has g_child_watch_add() (ChildWatch.add()), which sets a function to be called (child_finished()) when a child exits, and was the best approach for my case and the one I decided to replace the spinning technique with:
public void files_share () {
   try {
      Pid child_pid;
      if (Process.spawn_async("/", argv, null,
                                             SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
                                             null, out child_pid))
      {
         ChildWatch.add (child_pid, child_finished);
         if (num_children == 1)
            window.get_window().set_cursor(new Gdk.Cursor(Gdk.CursorType.WATCH));
      }
   }
}
void child_finished (Pid pid, int status) {
   Process.close_pid(pid);
   if (num_children == 0)
      window.get_window().set_cursor(new Gdk.Cursor(Gdk.CursorType.LEFT_PTR));
}
All of these has been totally new for me and I am very happy I learned some bits of it. It definitely improved the code a lot!

Hopefully, all these changes will be merged to master very soon. I will keep you informed!