Ikke's Blog

Archives for: May 2005

May 29
OPluginManager

I've been working on my OPluginManager lately (not too much, got to study too, and watched the Queen Elisabeth concours of course ;-)).

This is what I got now (it's my first GTK code ever, so I'm quite happy with it):

This is the "plugin" code:

#include "plugins/o-plugin-manager-plugin.h"
#include "plugins/o-plugin-manager-plugin-data.h"

#define TEST_PLUGIN_MSG "[test-plugin] "

static gpointer plugin_init(gpointer d, GError **e) {
        g_debug("" TEST_PLUGIN_MSG "Init");
        return (gpointer) g_strdup("Test plugin data");
}

static void free_data(gpointer d) {
        g_debug("" TEST_PLUGIN_MSG "Freeing data \"%s\"", (gchar *) d);

        g_free(d);
}

static OPluginManagerPluginAuthorData author = {
        O_PLUGIN_MANAGER_PLUGIN_DATA_AUTHOR_MAGIC,
        "ikke",
        "eikke eikke com"
};

static OPluginManagerPluginData plugininfo = {
        O_PLUGIN_MANAGER_PLUGIN_DATA_MAGIC,
        "test-plugin",
        "A simple test plugin",
        "This is a sample test plugin, to test the OPluginManager functionality",
        "0.1",
        "http://www.eikke.com",
        &author,

        NULL,
        plugin_init,
        free_data
};

O_PLUGIN_MANAGER_PLUGIN_REGISTER(plugininfo);

As you can see it's braindead easy to write a plugin :-)

The code is quite a mess at the moment, the UI is just plain ugly and inaccessible, there's no I18N support, but well, as you can see, one day it might work :-)

Lots of guys I know are in Stuttgart right now, visiting GUADEC. I wish I could be there too :-( Have a great time!!!

May 29
GVim

Just had a little chat with Ruben on Vim, where he suggested me to put my .vimrc etc online.

So I did, this is my current GVim setup:


(Thumbnail is link to original image)

And here's my current .vimrc. If you know any other fancy tricks I could use, please let me know :-)

A lot of useful information on configuring your Vim is available here.

May 25
Nokia

Ok, I admit, I also want one. Just check Planet Gnome if you don't know what I'm talking about.

May 24
Gnome stuff

Some new Topaz mockups were created, showing another (more radical IMHO) view on what Gnome3 should become, like these before. If you want to comment on them, desktop-devel is the place to be.

Since today, I got write access to Gnome CVS, mostly thanks to Philip and Ronald. Thank you guys! Just got the documentation building of asyncworker working (I hope at least ;-)), patch submitted (edit: and committed. My first one :-)). I'll import my first work on OPluginManager somewhere later this week.

And I may not forget to send out some emails to find some holiday job. As I'm a lazy bum, I still didn't send them :oops:

May 24
OModuleLoader

It looks like I have to release the current code I got for OModuleLoader/OPluginManager. Well, here it is. Still needs a lot of love, but hey ;-)

May 22
C preprocessor magic

Did you ever run in this situation where you need to #define some string, and use it both as a function name and as a string? As you might have discovered it is not easily possible to do this.

I ran into this problem today, and found out you got to solve it using 2 extra macro's (thanks to this page):

#include <stdio.h>

#define xstr(s) str(s)
#define str(s) #s

#define FOO foobar

void FOO() {
        printf("In %s\n" xstr(FOO));
}

int main() {
        /* This won't work because FOO is no string */
        /* printf("FOO is \"%s\"\n", FOO); */
        printf("FOO is \"%s\"\n", xstr(FOO));
        FOO();
        return 0;
}

which gives the desired result:

FOO is "foobar"
In foobar

and inspecting the output of nm, the function is correctly called "foobar", not "FOO". Jay!

[edit] Once more I just got blown away by what glib offers you. G_STRINGIFY is defined by default when you include glib.h (in glib/gmacros.h more precisely).

May 21
GModuleLoader

After Philip was talking about his GQueueThread/ASyncWorker on #gnome-nl, I was tempted to write some Glib-extension too, and so I did.

You might remember the extra exercise in my article on GModules and vtables, about loading all modules in the current directory and use them. Well, I created a generic object to handle this, because it's a functionality needed quite frequently in plugin-based programs.

I called this "GModuleLoader" ;-)

Here's a sample client application, loading 2 modules:

#include <glib.h>
#include <gmodule.h>

#include "g-module-loader.h"
#include "g-module-loader-module.h"

gint main(gint argc, gchar *argv[]) {
        GModuleLoader *l = NULL;
        guint cnt = 0;
        GPtrArray *modules = NULL;

        g_type_init();

        /* Create a new GModuleLoader */
        l = (GModuleLoader *) g_module_loader_new();
        g_assert(l != NULL);

        /* Load all modules in ".libs", with "simple-test" as data.
         * We do not want any error reporting (yet) */
        g_module_loader_load(l, ".libs", "simple-test", NULL);

        g_print("\n\nLooping through all loaded modules, printing the data\n");
        /* Get the list of all modules */
        modules = g_module_loader_get_modules(l);
        if(modules == NULL) {
                g_object_unref(l);
                g_error("No modules in list, aborting");
                return 1;
        }
        
        /* Loop */
        for(cnt = 0; cnt < modules->len; cnt++) {
                GModuleLoaderModule *m = NULL;
                gchar *data = NULL;
                GModule *mod = NULL;

                /* Get the GModuleLoaderModule */
                m = (GModuleLoaderModule *) g_ptr_array_index(modules, cnt);
                /* Normally we don't ever need the real GModule,
                 * because we use a vtable with all tables we need as returned
                 * data from the init function */
                mod = g_module_loader_module_get_module(m);
                if(m == NULL) {
                        g_error("Module in a GModuleLoaderModule should never be null");
                        return 1;
                }
                /* This is the data returned by the module init function.
                 * Most times this will be a vtable/struct */
                data = g_module_loader_module_get_module_data(m);

                g_print("%s:\t%s\n", g_module_name(mod), data);
        }
        g_print("\n");
        
        /* Clean up */
        g_object_unref(l);
        
        return 0;
}

I hope this is understandable :-)

Here's the code for "module1.c", the source for libmodule1.so. "module2.c" is almost the same: s/M1/M2 and s/module1/module2:

#include <glib.h>
#include <gmodule.h>

/* This is a fixed name, the module init function
 * It takes a pointer (given by the client code)
 * and can return some data (most of the time a vtable/struct, we keep it 
 * simple here) */
G_MODULE_EXPORT gpointer module_get_data(gpointer data) {
        gchar *s = NULL;
        
        s = (gchar *) data;
        if(s != NULL) {
                g_print("\n[M1] Module1 loaded with data \"%s\"\n\n", s);
        }

        return g_strdup("[M1] module1data");
}

/* Same thing, fixed name.
 * This function takes a pointer to the data given by module_get_data
 * and is called when this data should be freed */
G_MODULE_EXPORT void module_free_data(gpointer data) {
        gchar *s = NULL;
        
        s = (gchar *) data;
        if(s != NULL) {
                g_print("\n[M1] Freeing module1 data \"%s\"\n\n", s);
                g_free(data);
        }

        return;
}

As you can see I'm using fixed names in the plugins, I don't think there's any other way to do this kind of things. None of these 2 functions are required though.

Here's the output of the test application:

# ./simple-test
** (process:24094): DEBUG: Loading all modules in .libs, suffix is so
** (process:24094): DEBUG: Suffix for .libs/libmodule1.so is so, loading module
** (process:24094): DEBUG: Loading data for .libs/libmodule1.so

[M1] Module1 loaded with data "simple-test"

** (process:24094): DEBUG: Module .libs/libmodule1.so is valid, adding
** (process:24094): DEBUG: Suffix for .libs/libmodule2.so is so, loading module
** (process:24094): DEBUG: Loading data for .libs/libmodule2.so

[M2] Module2 loaded with data "simple-test"

** (process:24094): DEBUG: Module .libs/libmodule2.so is valid, adding


Looping through all loaded modules, printing the data
.libs/libmodule1.so:    [M1] module1data
.libs/libmodule2.so:    [M2] module2data

** (process:24094): DEBUG: Deleting 2 modules
** (process:24094): DEBUG: Freeing data for .libs/libmodule1.so

[M1] Freeing module1 data "[M1] module1data"

** (process:24094): DEBUG: Closing module .libs/libmodule1.so
** (process:24094): DEBUG: Freeing data for .libs/libmodule2.so

[M2] Freeing module2 data "[M2] module2data"

** (process:24094): DEBUG: Closing module .libs/libmodule2.so

It should be fairly easy to follow all steps, find out the module_get_data and module_free_data calls, etc.

It's working fairly well now, I'll try to enhance it some more, test it better and document it, then maybe I'll send some email to the Glib list ;-)

May 19
GModules and vtables

After the article I posted yesterday on GModules, I just wrote some sample code using a vtable to look up functions, which makes module handling much easier.

I guess we better go straight to the code:

First we need to define some structure that represents our vtable. I did this in a common header file, vtable-common.h:

#ifndef _VTABLE_COMMON_H
#define _VTABLE_COMMON_H

#include <glib.h>

typedef void (* voidvoidfunc) (void);
typedef gint (* intvoidfunc) (void);
typedef void (* voidstringfunc) (gchar *s);
typedef gboolean (* boolpstringfunc) (gchar **ps);
/* This function will demonstrate NULL function pointers */
typedef void (* foofunc) (void);

typedef struct VtableTest {
        voidvoidfunc funcone;
        intvoidfunc functwo;
        voidstringfunc functhree;
        boolpstringfunc funcfour;
        foofunc funcfive;
} VtableTest;

/* Module init function */
typedef VtableTest * (* VtableTestInit) (void);

#endif

First we define typedefs for all function prototypes we want in our vtable, then we define the prototype of a module init function.

This may look a bit strange, let's take a look at the module code (in vtable-module.c) to see what we can do with this:

#include <glib.h>
#include <gmodule.h>

#include "vtable-common.h"

/* Our vtable function implementations */
static void one() {
        g_print("[M] Function 1\n");
}

static gint two() {
        g_print("[M] Function 2\n");
        return 2;
}

static void three(gchar *s) {
        g_print("[M] Function 3: %s\n", s);
}

static gboolean four(gchar **s) {
        g_print("[M] Function 4\n");
        *s = g_strdup("four");
        return TRUE;
}

/* Our function table.
 * As you can see, we put functions in there as if they're normal variables */
static VtableTest table = {
        one,
        two,
        three,
        four,
        /* This module does not implement funcfive */
        NULL
};

/* This is the module init function.
 * It's a "VtableTestInit" function, as defined in vtable-common.h */
G_MODULE_EXPORT VtableTest * vtable_module_init() {
        g_debug("[M] Initializing module");
        /* Of course you can do a lot in this function,
         * we don't need to do anything in this sample */
        /* Return a reference to the function table */
        return &table;
}

The comments in the code should explain every step pretty well.
As you can see we only export one function, vtable_module_init. This function got a fixed name (so we should define this name in our API). It returns a pointer to a vtable of type VtableTest, which includes pointers to all function implementations.

Last but not least is the main code, in vtable-main.c:

#include <glib.h>
#include <gmodule.h>

#include "vtable-common.h"

gint main(gint argc, gchar *argv[]) {
        /* Same handles as yesterday */
        GModule *module = NULL;
        VtableTest *moduletable = NULL;
        gchar *modulepath = NULL, *dir = NULL;
        VtableTestInit moduleinitfunc = NULL;
        /* Function value */
        gint two = 0;
        gboolean fourb = FALSE;
        gchar *fours = NULL;
        
        /* Same stuff as yesterday */
        if(g_module_supported() == FALSE)
                g_error("No module support");

        dir = g_get_current_dir();
        modulepath = g_module_build_path((const gchar *) dir, "vtabletestmodule");
        g_debug("Module path: %s", modulepath);
        module = g_module_open(modulepath, G_MODULE_BIND_LAZY);
        g_free(dir);
        g_free(modulepath);
        if(module == NULL)
                g_error("Unable to load module");

        /* We need to lookup one function, which inits our vtable */
        if(g_module_symbol(module, "vtable_module_init", (gpointer *) &moduleinitfunc) == FALSE) {
                g_error("Unable to get reference to the module init function: %s", g_module_error());
        }

        /* Get a reference to the module function table */
        moduletable = moduleinitfunc();

        /* Run all our functions, providing parameters or fetching return
         * values where necessary */
        if(moduletable->funcone != NULL) {
                g_debug("Running module funcone");
                moduletable->funcone();
        }
        else {
                g_warning("Funcone is NULL");
        }

        if(moduletable->functwo != NULL) {
                g_debug("Running module functwo");
                two = moduletable->functwo();
                g_debug("functwo returned %d", two);
        }
        else {
                g_warning("Functwo is NULL");
        }

        if(moduletable->functhree != NULL) {
                g_debug("Running module functhree");
                moduletable->functhree("vtable-module-test");
        }
        else {
                g_warning("Functhree is NULL");
        }

        if(moduletable->funcfour != NULL) {
                g_debug("Running module funcfour");
                fourb = moduletable->funcfour(&fours);
                g_debug("funcfour returned \"%s\", string value is \"%s\"", fourb == TRUE? "true" : "false", fours);
        }
        else {
                g_warning("Funcfour is NULL");
        }

        if(moduletable->funcfive != NULL) {
                g_debug("Running module funcfive");
                moduletable->funcfive();
        }
        else {
                g_warning("Funcfive is NULL");
        }

        /* As yesterday, close the module */
        if(g_module_close(module) == FALSE) {
                g_error("Unable to close module: %s", g_module_error());
        }

        return 0;
}

These are the steps we take:

  • Load the module (see the previous article for more information on this)
  • Look up one symbol, "vtable_init_module". As mentioned before, this is the fixed name symbol that should be exported from our module.
  • run "vtable_module_init", so we get a reference to the module's vtable
  • Now we can use all functions refered to in the vtable. Make sure you always check for NULL pointers, or your application will crash. Even if your API/documentation states a module author must implement all vtable functions, checks don't hurt :-) Notice we call the functions using moduletable->foofunc(), so the vtable members are really normal function pointers, nothing fancy here.
  • We clean up by closing the module

You can compile everything with this simple Makefile (yes I know it's a bad one):

default: main libvtabletestmodule.so
all: default

main: vtable-main.c vtable-common.h
	gcc -o main -g `pkg-config --cflags --libs glib-2.0 gmodule-2.0` vtable-main.c

libvtabletestmodule.so: vtable-module.c vtable-common.h
	gcc -o libvtabletestmodule.so -g -shared `pkg-config --cflags --libs glib-2.0 gmodule-2.0` vtable-module.c

or execute the commands by hand, of course.

Here's the output:

** (process:16338): DEBUG: Module path: /home/foo/bar/vtable/libvtabletestmodule.so
** (process:16338): DEBUG: [M] Initializing module
** (process:16338): DEBUG: Running module funcone
[M] Function 1
** (process:16338): DEBUG: Running module functwo
[M] Function 2
** (process:16338): DEBUG: functwo returned 2
** (process:16338): DEBUG: Running module functhree
[M] Function 3: vtable-module-test
** (process:16338): DEBUG: Running module funcfour
[M] Function 4
** (process:16338): DEBUG: funcfour returned "true", string value is "four"

** (process:16338): WARNING **: Funcfive is NULL

As you can see, all this is quite logical and easy to write once you figure out how to. The provided functionality can be very useful though.
Notice thanks to glib, the code presented here should compile and run under Linux, Solaris and all other supported platforms (yes, even on Windows using DLL's) without any code change.

A little exercise for the reader: currently we got the module name "vtabletestmodule" hardcoded in vtable-main.c. What to do if we have several VtableTest implementations, e.g. vtabletestmodule1 and vtabletestmodule2? We could loop through all files in ".", find out whether they're a valid module (ie try to load them), if they're a valid module, try to figure out whether it exports "vtable_module_init", if that's the case, get a reference to the module's vtable, and use it. This is eg the way Gaim loads all it's plugins (although the module files aren't stored in "." of course). It's not too difficult to implement this, but the result is quite impressive too, so give it a try :-)

May 18
GModules are fun

Glib's GModules are fun :-) These functions provide a braindead interface to modular (think "plug-in based") programming. I wrote some testing code today (read the API a year ago or so but never played with it), here's a short introduction.

When writing module based applications there are mainly 2 parts: the modules, and the application using them. So we'll have to write these 2 things.

Getting started with the module is the easiest part:

#include <glib.h>
#include <gmodule.h>

G_MODULE_EXPORT void m_helloworld() {
        g_print("Hello modular world!\n");
}

This code is pretty straight-forward. The only "strange" thing is G_MODULE_EXPORT, a platform-independent macro telling the compiler/linker to export the function.

Then comes the "client" application, a little more difficult. Comments inline:

#include <glib.h>
#include <gmodule.h>

/* A prototype of the function pointer we'll use */
/* void function(void) */
typedef void (*HelloWorldFunc) (void);

gint main(gint argc, gchar *argv[]) {
        /* We need:
         * - A handle to our module
         * - A pointer to the function we'll import
         * - Some helper strings */
        GModule *module = NULL;
        HelloWorldFunc hello = NULL;
        gchar *module_path = NULL, *curr = NULL;

        /* Check whether glib is compiled with module support */
        if(g_module_supported() == FALSE) {
                g_error("Modules not supported :(");
                return 1;
        }
        
        /* We need to figure out the path to our module. In our test case, this
         * is ".", so we want the current dir. */
        curr = g_get_current_dir();
        /* Create the path to the module. This function does quite a lot of
         * of things, check the GModule API. */
        module_path = g_module_build_path((const gchar *) curr, "module");
        /* Don't we love debugging? */
        g_debug("Module path: %s", module_path);

        /* Finally we're able to open the module. We want lazy symbol resolving.
         * This means we only want a symbol to be resolved if we request it.
         * Once more, see the API for more information. */
        module = g_module_open(module_path, G_MODULE_BIND_LAZY);

        /* Get rid of those helper strings */
        g_free(module_path);
        g_free(curr);

        /* Check whether the module was loaded successfully */
        if(module == NULL) {
                g_error("Unable to load module");
                return 1;
        }

        /* Load the symbol and assign it to our function pointer. 
         * Check for errors */
        if(g_module_symbol(module, "m_helloworld", (gpointer *) &hello) == FALSE) {
                g_error("Unable to get function reference: %s", g_module_error());
                return 1;
        }

        /* Now we can call our funtion.
         * As you can see, we can call it as if it's a normal function.
         * Don't we love function pointers? */
        hello();

        /* We're nice citizens and close all references when we leave */
        if(g_module_close(module) == FALSE) {
                g_error("Unable to close module: %s", g_module_error());
                return 1;
        }

        return 0;
}

(this looks like a lot of code (well...) but if you strip all comments and debugging stuff/checks, you'll only have 10 lines or so)

Should be quite easy to understand too.

Now it's compile time. Of course, in a real-world situation, we'd use autotools to compile our libraries, we'd have a libtoolized library etc etc etc. Here we'll do it in the quick-and-dirty way:

# gcc -o libmodule.so -shared `pkg-config --libs --cflags glib-2.0 gmodule-2.0` module.c
# gcc -o main `pkg-config --libs --cflags glib-2.0 gmodule-2.0` main.c
# ls
libmodule.so  main  main.c  module.c
# ./main
** (process:26533): DEBUG: Module path: /home/foo/bar/libmodule.so
Hello modular world!

Loading all these symbols by hand is a boring task, so most of the time you'll create some API using vtables to make your life easier. More on this later (got to study now ;-) :|)

May 18
Portage on Windows

Maybe one day we'll be able to get lots of *nix software running under Windows using Gentoo's Portage. Read this.

Ikke • LifePermalink 7 comments
May 17
Sysstats.sh

I wrote a little Bash script this morning, which gives a quick overview of your system.
As mentioned in the help text, this can be very usefull for server administrators, eg allowing you to let an overview be sent to your mailbox every 24h using a cronjob.

This is a sample of the current output:

================================================================
* System statistics for:        foo.baar.be
* Generated on:                 Tue May 17 15:00:49 CEST 2005
* Running as:                   root

=================== Uptime =====================================
* Uptime:                       4 days, 04:37:29
* High score:                   151 days, 19:15:18
* Load average:                 0.32 0.24 0.22

=================== Users ======================================
* Active users:                 2
* User information
        USER     TTY        LOGIN@   IDLE   JCPU   PCPU WHAT
        someone pts/0     12:15    2.00s  0.62s  0.05s sshd: ntrangez [priv]
        someone pts/1     13:30    5:19   1.17s  0.05s sshd: ntrangez [priv]

=================== Tasks ======================================
* Number of running tasks:      62
* Running programs:
agetty          events/0        kjournald       lockd           portmap         sh
aio/0           fcron           kseriod         mysqld          ps              sshd
apache2         init            ksoftirqd/0     mysqld_safe     rpc.mountd      su
bash            kblockd/0       kswapd0         nfsd            rpc.statd       syslog-ng
boa             khelper         kthread         pdflush         rpciod/0        uptimed

* CPU load:                     2%
* Process state:
        Running:                1
        Sleeping:               60
        Zombie:                 0
        Stopped:                0

=================== Networking ================================
* Routing table:
        Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
        192.168.2.0     wifi.vtk.ugent. 255.255.255.0   UG    0      0        0 eth0
        192.168.1.0     *               255.255.255.0   U     0      0        0 eth0
        loopback        webvtk.vtk.ugen 255.0.0.0       UG    0      0        0 lo
        default         fire.vtk.ugent. 0.0.0.0         UG    0      0        0 eth0

=================== Hard Disc =================================
* Disk Information:
        Filesystem            Size  Used Avail Use% Mounted on
        /dev/hdc4             4.5G  3.2G  1.1G  75% /
        /dev/hdc5              14G   11G  2.4G  82% /var/www

* Mounts:
        /dev/hdc4 on / type ext3 (rw,noatime)
        none on /proc type proc (rw)
        none on /sys type sysfs (rw)
        none on /dev type ramfs (rw)
        none on /dev/pts type devpts (rw)
        /dev/hdc5 on /var/www type ext3 (rw,noexec,nosuid,nodev,noatime,acl)
        none on /dev/shm type tmpfs (rw)
        nfsd on /proc/fs/nfs type nfsd (rw)

=================== MD5Sum Check ==============================
* Checking MD5 Sums
        /etc/passwd:                            [OK]
        /etc/shadow:                            [OK]
        /etc/group:                             [OK]

===============================================================
* Brought to you by Ikke - http://www.eikke.com

A TODO is included in the script source.

I made the script available here (enscript version here). If you use it, please let me know something :-) Or even better, if you add more support, please send me a patch so everyone can make use of it :-)

It has no special dependencies, next to Bash and some standard utilities like wc, bc, tail, md5sum, awk and grep.
The "record uptime" is only displayed if you got uprecords (/usr/bin/uprecords) installed.
Make sure you check the md5sum part, can be very usefull :-)

May 16
Anjuta 2

A 2.x alpha release of Anjuta, the GTK/Gnome-oriented IDE, has been released.
I must admit the screenshots and features look nice: integrated debugger (GDB based), glade3 support, GObject class building wizard, member completion,...

Maybe time to give it another test drive. Although I doubt it can beat gVim :-) What do you think, Realnitro?

May 15
ContactFS working


(Thumbnail is link to original image)

Needs no comment I guess :-)

Still some problems left:

  • Files with spaces in the filename (ie almost every file in this case) don't work :-( This shouldn't be too hard to solve though (edit: fixed)
  • Read-only
  • No VCard as output. This can be easily solved though (edit: fixed)
  • The init func creating a Node tree from your contacts is blocking, which is bad
  • The Icon issue. Looks like vcards can contain picture information, so I could just write a vcard-thumbnailer and add it to Nautilus.

Now guess what the problem was... Not my code, but the fact I use autotools to compile the thing. Autotools seems to add some fancy compiler flags which made the module crashy as hell, just using "gcc -Wall $(pkg-config --cflags --libs gnome-vfs-module-2.0 libebook-1.2) -shared -fPIC ContactFS.c -o libContactFS.so" worked fine. Guess I'll have to tweak my Makefile.am somehow. (edit: this wasn't autotools fault, but (of course) mine. I added some flags to Makefile.am I got from an official GnomeVFS module's Makefile.am, looks like those weren't nice)

Huge thanks to "gicmo" for all the help.

Oh, and XNest rocks :-)

[update]

May 15
ContactFS progress

Screenshot shows some progress I made with ContactFS. If I "browse" to contact:/, I get 2 "directories", Personal and Test, which are my 2 Evolution address books. I can "cd" into "Test", "ls" all "files" (contacts) in there, and if I try to "cat" one of them, I get some information on that contact (just plaintext for now, this should become a FOAF or VCard formatted string).

In GnomeVFS' test-shell application the module works fine, but if I try to open contact: in Nautilus, it crashes. Maybe I should send an email to Alex to beg for a little help here ;-)
I'll also need his help on how to set file thumbnails once it works in Nautilus (the actual file is just plain text, but I'd like to have the contact's picture as icon in Nautilus, or if no picture is set, /usr/share/pixmaps/nobody.png).

Moving "files" between "directories" would be nice too, but I don't know whether EDS allows writing, need to figure that out.

May 13
ContactFS

I got the insane (well, not really ;-)) idea today to write a GnomeVFS module that allows you to browse your Evolution EDS contacts using any GnomeVFS-capable application, like Nautilus, as a Proof Of Concept. This is the first GnomeVFS module I write.

Started working on it, and I got a basic module working. It does not get any information from EDS yet, only contains one "file" named JohnDoe, can be very instable,... but well ;-)

Here's a little screenshot:

Currently the "contact files" only have "test" as content, this should become a VCard string, or maybe a FOAF document.

Other things to do:

  • assign a proper MIME type, so I can take advantage of Evo's "Picture" contact-property (so you see the picture as "icon" of the "file", if this is possible at all).
  • assign a proper handler to the "open" action (e.g. open a Mailto: window)
  • Fetch real information from Evo, of course
  • Make it stable (the current tree contains dead pointers, I need to learn using GNode's) and thread-safe
  • ...

Maybe this won't be really usefull, but someone suggested to have some VFS module for Soylent too, so I'm gaining some experience here :-)

May 13
Regarding Microsoft

Although I'm hardly using any Microsoft software anymore, I still admit they got some great things, like their .Net framework and the corresponding IDE, VS.Net (don't hate me now).
This could be some great proof of concept/research technology too.

On the other hand: what did they smoke?

May 12
FeedMerge mostly done

JAY!!!! :-D

As I blogged before I've been working on a PlanetPlanet implementation in C. I got a basic RSS2 parser working before, but that code was plain ugly.

Now I refactored all code using GObjects (learnt a lot whilst doing this), and it works :-D
Currently it can parse RSS2 feeds (as many as your memory allows you to ;-)) and combine them into one RSS2 feed. Adding more feed types should be fairly easy.

This is a sample result, and here you can see a FeedValidator validation result. Never mind the encoding issue, that's related to the server settings of the machine I'm hosted on (and I'm no administrator :-().

This is the code I used to generate the feed:

#include <glib.h>
#include <libxml/tree.h>

#include "src/feedmerge-feed.h"
#include "src/feedmerge-merge.h"

gint main(gint argc, gchar *argv[]) {
        FeedMergeFeed *feed1 = NULL, *feed2 = NULL;
        GSList *feeds = NULL;
        xmlDoc *doc = NULL;

        g_type_init();

        feed1 = (FeedMergeFeed *) feedmerge_feed_new();
        feed2 = (FeedMergeFeed *) feedmerge_feed_new();

        feedmerge_feed_fetch_feed(feed1, "http://blog.eikke.com/xmlsrv/rss2.php?blog=1");
        feedmerge_feed_fetch_feed(feed2, "http://blog.eikke.com/xmlsrv/rss2.php?blog=7");

        feedmerge_feed_parse_document(feed1);
        feedmerge_feed_parse_document(feed2);

        /* feedmerge_feed_dump(feed2); */
        feeds = g_slist_append(feeds, feed1);
        feeds = g_slist_append(feeds, feed2);

        /* output type, GSList containing the feeds, feed title, URI and description */
        doc = feedmerge_merge_merge(FEEDMERGE_FEED_TYPE_RSS_200, feeds,
            "Test feed", "http://blog.eikke.com/ikke", "This is a test merged feed");

        g_assert(doc != NULL);

        /* output to stdout */
        xmlSaveFormatFileEnc("-", doc, "UTF-8", 1);

        xmlFreeDoc(doc);

        g_object_unref(feed1);
        g_object_unref(feed2);
        g_slist_free(feeds);
}

Using gob2 again to write my objects, it's just a pleasure to play with :-) Strong type checking is automagically added etc, just great. I'm eager to see some GObject introspection samples.

Of course what I got now can be further abstracted to something like this:

xmlDoc *doc = feedmerge_vamerge("http://a.b/feed1.xml", "file:///tmp/feed2.xml",
    "scp://somehost:/test.xml", "Title", "http://www.eikke.com", "This is a nifty combined feed");

i.e. using varargs.

Thanks to GnomeVFS I don't have to care where the feed comes from, which is just great.

I had to handle some nasty and stupid bugs while coding this (in my own code, of course) and propably I leak memory at several places too, but well... ;-) We got valgrind :-) And thanks to the GCC people for blessing us with the "-Werror" flag, I love that feature.
I won't publish the code yet, but maybe it'll get into Soylent (who knows?) so then it'll be in some CVS/SVN/whatever repository.

Regarding Soylent: we're looking for a new name. Please help us out, I can't wait to start doing some real work.

That's it for now.

May 7
MSN Webcam functionality part II and minor coding

Why didn't I hear about this any earlier, and why isn't it integrated into Gaim?

Oh, for Soylent, I started working on blog feed reading. Currently I got some code that can read an XML feed from any GnomeVFS source, and create a libxml2 xmlDoc out of it. Although maybe I should use SAX to merge different feeds...
Using an XSLT sheet to merge feeds would be really cool and clean, but I think it's the hardest way too ;-)
I tried to document the code well again, it's available here. Can be a nice introduction to GnomeVFS coding :-) (this was my first code using it too ;-))

TODO: add a check to see whether what we pull in is an RSS feed, before loading the whole document, to save bandwidth and memory.

May 6
First EDS code

I started looking into evolution-data-server today, to look what we could use in Project Soylent (actually, I shouldn't blog on it because we don't want any noise on it yet :-)). The result is some code that can dump all your address books, or only dump some of the contacts using a filter string.

Because libebook is very poor documented (unfortunately, even the API docs aren't complete) I tried to document the code as good as possible. Please review it, let me know when some things are not well documented, or I just make dumb mistakes in the code :-D
The last version I made is here.

Compile it using
gcc -o evo-addressbooks-test2 `pkg-config --cflags --libs libbonobo-2.0 libebook-1.2` evo-addressbooks-test2.c
Dump your address books using
./evo-addressbook-test2
or use a filter string as first argument. Beware: you should Bash-escape the argument!!!
A sample of this:
./evo-addressbooks-test2 "(contains \"full_name\" \"John Doe\")"
will list some properties of the entry which has "John Doe" as it's full name.

I hope I'll be able to hack on this some more, and also hope we'll get a good view of what Soylent should become soon :-)
It'll be a great oportunity to learn some more aspects of Gnome coding, like GTK/Glade, Bonobo (|-|) and others.

May 6
New blog engine and Soylent

As I mentioned before, I'd like to start using another blogging engine as soon as possible. Plog looked nice, but after giving it a test drive I got into some little annoying problems.
Now the b2evolution guys seem to prepare a maintainance version, so development hasn't completely stalled.

Currently, the blogs (mine and the others I host) run fine I think, and migrating would be quite a lot of work (existing users and posts, not to mention changes of URI's towards aggregators), so I guess I'll wait some longer, unless someone knows a very good engine I could start using, actively maintained.
The main "issue" I got is the fact I host multiple blogs. Most engines don't support this...

Project Soylent is getting started, jay :-) Just read something on using Evolution's libebook API and FOAF, this will be some fun project to work on B-)

Oh, and please everyone vote for davyvandenbremt.be on may1reboot.com.

May 4
Southpark Character


Do what others do

Maybe I should just use this as my hackergotchi, I'd scare people by using a "real" one ;-)
(PNG version now :-))

Ikke • LifePermalink 4 comments
May 2
Why

Karel, why oh why did you this? People seem not to understand I'm not intending to run MSN7 as my default Messenger at all, just trying to solve some little annoyance? Do they actually read what I wrote?

Just tried amsn-cvs. It crashed when I tried to start a normal conversation with someone, then worked fine. I did not try videochat support yet though, none of my contacts owning a webcam came online untill now.

May 1
MSN7 under Linux


(thumbnail is link to full size screenshot)

I want MSN Video Chat (receipt) working. Under Linux. As soon as possible. So I tried getting MSN Messenger working under Wine.

As you can see, it works. But only a little. This is the bloated version 7 of the official client, and Wine seems to be unable to handle it properly. It's using non-standard windows (actually, it are clipped standard windows) etc. I did not try the webcam feature either.

Has anyone got an installer for MSN Messenger 6.x for Windows 98/Me? I'd like to try that one too, but only "beta" version 7 is available on the official website. A commercial software vendor only offering beta versions of it's software to the users... :roll:

Maybe I should just run a stripped Windows session under that great version 5 of VMWare Workstation as RubenV just suggested, and take the easy way ;-) Or try aMsn which should get videochat support soon (it's already in CVS I heard, Scapor?), but I hate that Tcl/Tk interface :-(

Categories

Who's Online?

  • Guest Users: 462

Misc

XML Feeds

What is RSS?