Use nocase string as g_hash_table's key

/* ghashtabledemo.c -- GHashTable demo */

#include <glib.h>
#include <string.h>

void print_entry(gpointer key, gpointer data, gpointer user_data)
{
  /* user_data not used */
  g_print("key: %-10s     value: %-10s\n", (gchar *)key, (gchar *)data);
}

guint g_str_case_hash (gconstpointer key) {
 gchar *upkey = g_ascii_strdown (key, strlen(key));
 guint hash = g_str_hash (upkey);
 g_free (upkey);
 return hash;
}

gboolean g_str_case_equal (gconstpointer a, gconstpointer b) {
 if (!g_ascii_strcasecmp (a, b))
  return TRUE;
 else
  return g_str_equal (a, b);
}

int main(int argc, char *argv[])
{
  GHashTable *hash1;
 
  hash1 = g_hash_table_new(g_str_case_hash, g_str_case_equal);

  /* insert a bunch of entries */
  g_hash_table_replace(hash1, g_strdup("foo"), g_strdup("bar"));
  g_hash_table_replace(hash1, g_strdup("FoO"), g_strdup("BAR"));
  g_hash_table_replace(hash1, g_strdup("more"), g_strdup("junk"));

  /* print the contents */
  g_print("Hash table entries:\n");
  g_hash_table_foreach(hash1, print_entry, NULL);

  return 0;
}

/*
 * gcc -ansi -Wall `pkg-config glib-2.0 --cflags` -o test test.c  `pkg-config glib-2.0 --libs`
 */

How to add a menuitem in Nautilus context menu.

1. In src/file-manager directory, add command section and menuitem section in nautilus-directory-view-ui.xml.

<commands>
    ...
    <cmd name=”Name Convert”
        _label=”Name _Convert”
        _tip=”Convert the file name”/>
    ...
</commands>

<popup name=“selection“ tearoff=“0“>
    ....
    <placeholder name=“File Actions“ delimit=“top“>
        ...
       <menuitem name=”Name Convert” verb=”Name Convert”/>
        ...
    </placeholder>
    ...
</popup>

2. In fm-directory-view.c:

    #define FM_DIRECTORY_VIEW_COMMAND_NAME_CONVERT    “/commands/Name Convert“

    In real_merge_menus () function, BonoboUIVerb verbs [] array, add the following item:
    BONOBO_UI_VERB (“Name Convert“, name_convert_callback),

    You could use nautilus_bonobo_set_sensitive () or nautilus_bonobo_hidden () to show or hide this menuitem.

3. How to launch the corresponding program in callback function:

    GnomeVFSMimeApplication *test;
   
    test = g_new0 (GnomeVFSMimeApplication, 1);
    test->id = g_strdup (“test“);
    test->name = g_strdup (“this is a test“);
    test->command = g_strdup (“test“);
    test->expects_uris = GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_PATHS;
   
    file = NAUTILUS_FILE (selection->data);
    nautilus_launch_application (test, file, NULL);

    g_free (test->id);
    g_free (test->name);
    g_free (test->name);
    g_free (test);

    You could use gnome_vfs-get_local_path_from_uri (nautilus_file_get_uri (file)) to get the actuall file name.

Java动态代理的示例

public interface SomeClass {

    public abstract void someMethod();

    public abstract void someOtherMethod(final String text);
}

public class SomeClassImpl implements SomeClass{

    private String userName;

    public SomeClassImpl(final String userName) {
        this.userName = userName;
    }

    public void someMethod( ) {
        System.out.println(this.userName);
    }

    public void someOtherMethod(final String text) {
        System.out.println(text);
    }
}

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MethodCountingHandler implements InvocationHandler {

    /* whatever object, you can pass one in */
    private final Object impl;

    private int invocationCount = 0;

    /* constructor */
    public MethodCountingHandler(final Object impl) {
        this.impl = impl;
    }

    /* export the invocation Count */
    public int getInvocationCount( ) {
        return invocationCount;
    }

    /* implements the interface function of InvocationHandler */
    public Object invoke(Object proxy, Method meth, Object[] args)  throws Throwable {
        try {
            this.invocationCount++;
            Object result = meth.invoke(impl, args);
            return result;
        } catch (final InvocationTargetException ex) {
            throw ex.getTargetException( );
        }
    }
}

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class SomeClassFactory {

    public static final SomeClass getDynamicSomeClassProxy( ) {

        /* get a implement instance of SomeClass */
        SomeClassImpl impl = new SomeClassImpl(System.getProperty("user.name"));

        if ( !(impl instanceof SomeClass) )
            return null;

        /* construct a invocation handler with the impl instance */
        InvocationHandler handler = new MethodCountingHandler(impl);

        /* get the class info, and the class loader used by this factory */
        Class[] interfaces = new Class[] { SomeClass.class };
        ClassLoader loader = SomeClassFactory.class.getClassLoader( );

        /*
        * install the handler for all implementations of this interface in this class loader
        * and return the proxy instance which accords to SomeClass interface.
        */
        SomeClass proxy = (SomeClass)Proxy.newProxyInstance(loader,
                interfaces,
                handler);

        return proxy;
    }

}

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class DemoDynamicProxy {

    public static final void main(final String[] args) {

        SomeClass proxy = SomeClassFactory.getDynamicSomeClassProxy( );

        proxy.someMethod( );

        proxy.someOtherMethod("Our Proxy works!");

        /* get the handler associated with this proxy instance */
        InvocationHandler handler = Proxy.getInvocationHandler(proxy);

        if (handler instanceof MethodCountingHandler) {
            System.out.println(((MethodCountingHandler)handler).getInvocationCount( ));
        }
     }

}

Java的动态代理只支持基于Interface的Method Interception. 本例是从”Hardcore Java”一书中摘取出来的.

Monty Python和Monty Python Quote

我在翻译"Struts Kick Start"时遇到Monty Python Quote,不解何意,最后终于google到下面这篇文章,看来Monty Python团体对市井文化有很大的影响 🙂

"MONTY PYTHON"(巨蟒)小组成立于60年代后期,成员包括Graham Chapman, John Cleese, Terry Gilliam, Eric Idle, Terry Jones,和Michael Palin。这六位成员在组团之前已经彼此熟识,首先是在大学时代-Graham,John和Eric同在剑桥就读,Terry J和Michael则是在牛津;后来他们又在一些电视喜剧节目中互相有过合作,例如著名的The Frost Report。 1967年,John和Graham合力创作并出演了“At Last, the 1948 Show”节目,同时出演的还有Marty Feldman, Tim Brooke-Taylor和Aimi MacDonald,Eric也在剧中时有出现。第二年,Eric,Michael和Terry J合力创作了儿童节目”Do Not Adjust Your Set”(DNAYS),出演的还有David Jason2和the Bonzo Dog (Doo Dah)乐队(乐队中的Neil Innes后来成了PYTHON附属成员中重要的一员),更重要的是,Terry Gilliam负责了此节目的动画部分。

1969年初,Michael和Terry J合写并出演了一出名为”The Complete and Utter History of Britain“的系列现场短剧。John也参与了此剧,当时他已经决定要与Michael进行合作。 BBC的一位制作人Barry Took为两人安排了一次会面。 John带上了自己的写作伙伴Graham,Michael也带上了DNAYS剧组的同事Terry J,Eric和Terry G。 六人一碰到一起就擦出了火花(他们都热爱The Goons组合和Spike Milligan的Q5电视节目),BBC毫不犹豫地提供了经费,让他们制作一个十三集的电视节目。

The Flying Circus 飞行的马戏团

第一集”Monty Pythons Flying Circus“于1969年10月5日播出,节目被搁在了午夜时段,并且还经常得为其他节目的时间安排而随意挪动,英国有些地区的观众甚至根本就看不到它。 尽管如此,节目还是获得了一定的口碑,这足以让BBC决定在70年继续制作第二季。

就在第二季开播之前,Python们开始涉足电影,推出了”And Now For Something Completely Different“。这部电影的主要目的是让Python打开美国市场,可惜结果并不理想,小组又回到了电视上。第三季于1972-1973年期间播出,在第三季的最后, John决定不再参与Flying Circus节目。 第四季(名字改成了简单的Monty Python) 于1974年播出,仅仅维持了6集,被大多数人认为是整个系列中最差的几集。

电影

吸取了上一部电影”And Now For Something Completely Different“的教训,Python们决定在下一部电影中要有完全的自主权。

在第三季和第四季Flying Circus节目的拍摄间隙,小组溜到了苏格兰,以很低的成本完成了”Monty Python and the Holy Grail“(巨蟒与圣杯)的拍摄工作,Terry Jones和Terry Gilliam担任了导演。尽管摄制过程中碰到了很多困难-尤其是苏格兰那糟糕的天气情况-小组还是完成了这部后来取得巨大成功的电影。更幸运的是,”Holy Grail“在美国上映时正值Python节目刚开始在美国流行,这一点大大促进了电影的成功,也坚定了小组以电影作为发展方向的信念。

接下来的Python电影就是那部引起了极大争议的”Monty Pythons Life Of Brian“(万世魔星),上映于1979年。电影的点子来源于Eric Idle的一句漫不经心的回答,他声称他们的下一部电影将叫做“Jesus Christ: Lust for Glory“(耶稣基督:荣耀的渴望)。经过多次改写后,最后的剧本已经几乎和耶稣本人毫无关系了,而是将故事集中在了一名被错认成了救世主的青年身上。由于原先的出资方EMI的退出,电影一度很有可能要被搁浅。幸运的是,乔治哈里森(前披头士成员),这位Python的粉丝,实在是太想看到这部电影了,为此,他冒着倾家荡产的危险,创立了自己的制片公司” Handmade Films“,以资助这部电影。电影上映后遭到了一些信徒的强烈攻击,并在某些地区遭到禁映。尽管如此(或许是正因为如此),影片还是获得了巨大的成功。

接下来,Python又发行了名为“Monty Python live in Hollywood Bowl"的电影,记录了他们1980年在洛杉矶好莱坞碗体育馆的舞台演出。 小组表演了一些他们最受欢迎的小品,热闹的场面就像是一出摇滚音乐会。

Python们的下一部电影最终成了他们的最后一部电影。 ”Monty Pythons The Meaning Of Life“(人生七部曲)上映于1983年,同样获得了评论与票房的双丰收,还勇夺当年嘎纳电影节的评委会大奖。电影包含了一些最疯狂的Python式段落,包括那首攻击天主教反避孕政策的歌曲”Every Sperm is Sacred“(每粒精子都是神圣的),学校里的现场性教育课,Graham Chapman被一群近乎全裸的女子赶下了悬崖,当然还有那位呕吐不止的Creosote先生。

同时......

在忙于电视系列和电影的同时,Python们也参与了一些其他工作,尤其是一系列的著作和录音。这些作品和他们的电视、电影一样具有创新性,唱片”The Monty Python Matching Tie and Handkerchief“(巨蟒鞋带与手帕)还是世界上第一张”三面“唱片-唱片的反面被第二道凹槽分成了两部分,听到什么内容取决于唱针落在哪一部分。作为Python的传统,他们的书籍和录音同样引起了一些争议,书籍”Monty Pythons Brand New Bok5“的白封面上特意做了一个仿真的脏手印,在书店里引起了一定的混乱; 唱片”Monty Pythons Instant Record Collection“同样给店主们添了很多麻烦,Terry Gilliam设计的折叠式封套(组合起来后看起来就像是一叠唱片)太过复杂,经常被客户无意间弄断,后来发行的版本不得不换上了一种简单得多了的封套;唱片“Monty Pythons Contractual Obligation Album”(巨蟒合同所迫下的专辑)在另一种方面也引起了争议,其中的那段小品“Farewell to John Denver"(一小段Denver名曲”Annies Song"之后传来的是某人被扼死的声音)在后来的版本中被删去(替换成了一段空白和Terry Jones的致歉),而另外一首"Sit on My Face"由于涉嫌抄袭也险些遭到同样命运。

如今,Python组合以网站的形式继续维持着,他们的官方网站WWW.PYTHONLINE.ORG. 这个网站更像是Eric Idle的个人作品,但其他几人也偶尔会参与进来。当然,如今的大众文化中也到处都是对Python的引用,短语“this is an ex-parrot”和“nudge, nudge, wink wink, say no more”已经成了英语术语,单词“Pythonesque"也进入了英语字典,它是这么定义的:Pythonesque,形容词, 意指幽默的,奇异的和超现实主义的, 来源于BBC电视喜剧节目”Monty Pythons Flying Circus“。

科学家(尤其是计算机科学家)在命名新事物时经常会从Python上寻找灵感:网络术语”spam"(垃圾邮件)来自于一个Python小品,而编程语言 "Python“更是一种直接的盗用。 1985年发现的一块巨大的蛇化石也被命名为拉丁语”Montypythonoides riversleighensis6“(Riversleigh是化石的发现地)。

最后

尽管"Meaning of Life"最终成了真正意义上Python的最后一部作品,但贯穿整个八十年代,一直都流传着Python要重组的说法,另一出舞台表演,另一部电影,另一部电视系列剧.....。 问题是每个独立后的Python成员都忙于自己的(或是互相的)事业,很难找出时间来让他们六个人重新聚在一起。 1988年,Graham Chapman被诊断出患上了癌症,尽管他一度声称已经战胜了病魔,但最终还是于1989年10月4日去世,就在“Flying Circus”节目播出二十周年的前一天。

Python小组在1998年重组参加了在美国科罗拉多州Aspen的一次舞台演出,英国喜剧演员Eddie Izzard替代了Graham的位置。 Graham也“出现”在了舞台上(在一个骨灰盒中),演出结束后被Terry Gilliam”不小心地“踢倒撒开。此时关于新的电影或是舞台演出之类的说法又开始流传,但都未能实现。 1999年,小组又聚在了BBC,参与了三十周年的庆典。悲伤的是,尽管他们彼此依然是朋友,但他们各自都实在太忙了,以至于根本无法再重新聚到一起投入新的计划。 而且,没有了Graham Chapman,也不会再有真正的Python了......


How to create customized widget in glade project.

1. Create a new glade project, either Gnome or Gtk+.

2. In "Gtk+ Addtional" tab, click the "Custom Widget" button labeled with "C", and add this DUMMY widget into any container of a top-level window.

3. In "Properties" dialog, fill the "Name" field, and then fill the "Creation Function" field (e.g., create_gtksrcview), other arguments can be ignored.

4. Save the glade file, and DO NOT compile the glade to C/C++ source code.

5. Initialize the UI from glade file, and show the main window, as following:

ui = glade_xml_new("./sunny.glade", NULL, NULL);
main_wnd = glade_xml_get_widget (ui, "main_wnd");
gtk_widget_show (main_wnd);
glade_xml_signal_autoconnect(ui);

6. Define the customized widget create function in C file, the function name must equal to the one you setup in glade file, and this function will be invoked automaticlly by libglade, and at last, you must SHOW the widget you created.

GtkWidget*
create_gtksrcview (gchar *widget_name, gchar *string1, gchar *string2, gint int1, gint int2)
{
GtkWidget *view;
GtkSourceBuffer *buffer;
... ...
view = gtk_source_view_new_with_buffer (buffer);
... ...
gtk_widget_show (view);
return view;
}

IIIM server code reading note (part 2)


* Singleton -- the most commonly used pattern.
There are some singleton classes in IIIMSF such as IMLog, IMSignal
etc, and basicly follows the scheme below:

class Singleton
{
    static Singleton * _pInstance;

    Singleton ();
    vitual ~Singleton ();

public:
    static Singleton * get_instance ();

    static Singleton * construct ();
    static void * cleanup ();
};

It's not correct! At least, we should declare the copy constructor as
private like that:

private:
    Singleton (const Singleton& other);


* State pattern applied in IIIM
First, let's to see how the classic state pattern looks like.

+------------+              +-----------+
|   Context  |<>------------|   State   |
+------------+              +-----------+
| Request () |              | Handle () |
+------------+              +-----------+
                                  A
                                  |    
                    +-------------+----- ... ...
                    |             |
              +-----------+    +-----------+
              |  StateA   |    |  StateB   |
              +-----------+    +-----------+  (concrete states)
              | Handle () |    | Handle () |
              +-----------+    +-----------+

The member functions of concrete states, take the reference of
"Context" as one argument. Concrete states use this reference to call
it's change_state () function. Every concrete state knows what will be
the next state after a certain operation. And state object is usually
a singleton.

In IIIM, ICState is the "Context", IIIMP_ICState is the parent "State"
class, while IIIMP_ICState_REQUESTED, and IIIMP_ICState_WAITING
classes are the concrete state classes. In case of IMState, it's the
same.

IIIM server code reading note (part 1)

* UML static structure:
+----------+
| <<if>>   |---o request_accept ()
| IMAccept |---o request_connect ()
+----------+                          +--------+
     A                    +---------|>| IMAuth |
     |                    |           +--------+
     |                    |
 +-------+           +-----------+       +--------+
 | IMSvr |<>----+----| IMUserMgr |<>-----| IMUser |
 +-------+      |    +-----------+ 1   * +--------+
    |           |
    .           |    +-------+       +--------+        +--------+
    |           +----| LEMgr |<>-----| LEBase |-.-.-.->| libiml |
    .           |    +-------+ 1   * +--------+        +--------+
    |           |
    .           |    +------------+       +---------+       +---------+
    |           +----| IMProtocol |<>-----| IMState |<>-----| ICState |
    V                +------------+ 1   * +---------+ 1   * +---------+
+-------------+               A                 A
|<<singleton>>|---o start ()  |                 |
| IMScheduler |---o stop ()   |                 +-------------------------+
+-------------+               +--+                                        |
       A                         |                                        |
       |                         |                                        |
+------------------+          +--------------+       +----------------+   |
| IMScheduler_MTPC |-.-.-.-.->| IIIMProtocol |<>-----| IMSocketListen |   |
+------------------+          +--------------+ 1   1 +----------------+   |
        \                                <>                               |
         \                                |                               |
  +-------------------------------+       +----------map {IIMPTrans*, IIIMP_IMState}
 / Make a thread per connection.  |
| Invoke thread func IMScheduler_ |
| MTPC_thread_entry ()            |
+---------------------------------+
                                          +-------+
                               +-.-.-.-.->| LEMgr |
                               |          +-------+
+-----------+                  |
| <<if>>    |       +--------------+      +--------+
| IMHandler |<|-----| IMConnection |----->| IMUser |
+-----------+       +--------------+ 1  1 +--------+
                      |1   |1  |1            | 1
                      |    |   |             |
                      |    |   |             V *
                      |    |   |         +-----------+
                      |    |   +-------->| IMDesktop |
                      |    |           1 +-----------+
                      |    |
                      |    |             +-------+
                      |    +-------------| IMSvr |
    <<create>>        |                1 +-------+
 +--------------------+
 |
 |      +---------+       +-----------------+
 |      | ICState |<>-----| IMLExec_ICState |
 |      +---------+ 1   1 +-----------------+
 |          |
 |          . <>
 |          |
 V *        V
+---------------+
|   <<if>>      |----o send_event ()
|   ICHandler   |----o ...
+---------------+
      A
      |
+----------------+       +--------------+
| IMInputContext |<----->| IMConnection |
+----------------+ *   1 +--------------+
    |    | 1
    .    |               +-----------+        +-----------------------+
    |    +-------------->| LEContext |-------/ One ic may corresponds |
    .                  * +-----------+      | to several LE context   |
    |                                       +-------------------------+
    .          +-------+
    +-.-.-.-.->| LEMgr | Note: LEMgr should be a singleton?
    .          +-------+
    |
    .          +---------+
    |          | <<if>>  |       +-----------------+
    +-.-.-.-.->| IMLExec |<|-----| IMLExec_ICState |
               +---------+       +-----------------+

+------------+       +---------------+       +------------------+
| IIIMPTrans |<>-----| IMSocketTrans |<|-----| IMSocketTransTLS |
+------------+ 1   1 +---------------+       +------------------+ 

* Before connected: 1. IIIMProtocol::accept ()    IIIMProtocol::restart (), initialize the private member    IMSocketListen *pimsl.    use pimsl to accetp a client socket, and return an IMSocketTrans    instance, if passes the authorization, create an IIIMP_IMState    object and return. 2. IMScheduler_MTPC::start ()    create a pthread for this client's connection 3. IIIMProtocol::receive_and_dispatch ()    pthread loop IMScheduler_MTPC_thread_entry call this function to    handle client requests.    IIIMPTrans::receive () to receive an IIIMP_message, then call 4. 4. IIIMP_IMState::dispatch ()    if ic_id is invalid, call IMState::deliver (). Since the connection    has not been established, the IMState::pproc_state should be null,    so it then call IIIMP_IMState::message_proc (). 5. IIIMP_IMState::message_proc()    in case of IM_CONNECT message, get userinfo from request, call    IMSvr::request_connect (). This IMAccetp interface tries to    authorize this user, if successed, create an IMConnection and    returned as IMHandler*. Change imstate to IIIMP_IMState_Identified. 6. IIIMP_IMState_Identified::message_proc ()    If connected clients request an operation for im instead of ic,    (e.g., IM_CREATEIC, IM_DISCONNECT etc), this method will be    invoked, while in this case the ic_id must be invalid.    in case of IM_CREATEIC, call IMConnection::createic () to create an    IMInputContext instance and returned as ICHandler*. call    IIIMP_ICState::create(ic_id, this, pich) to create an icstate    instance, add it to icmap, then send a reply to client. * After connected: 1. IIIMProtocol::receive_and_dispatch () 2. IMState::dispatch ()    if ic_id is valid, from the IMState::IMShared::ICStateMap, look up    the matched ICState, then call ICState::deliver () to handle the    client's request or reply. 3. ICState::deliver ()    get current ICState, and then call its message_proc ().    IIIMP_ICState::start_request() will turn the current state to    IIIMP_ICState_REQUESTED. While IIIMP_ICState_REQUESTED::finish ()    will restore to previous state, and destory itself.    if current state is requested, its message_proc () will return    false, since it can not handle any request at that time.    if current state is waiting, its message_proc () will see whether    the incoming message is what it's waiting for. if not, it calls    IIIMP_ICState::message_proc () to handle this message, so in    waiting state will not block the normal request. or, it retrieves    and restore to the previous back-up request state, and then call its    dealing method. this method in trun calls get_imlexec()->execute(),
   this method execute all pending iml instructions.

   IIIMP_ICState_REQUESTED::wait ()
   IIIMP_ICState_REQUESTED::wait_aux ()
   These two methods can change the current icstate from "requested"
   to "waiting" (for the replies from client).

   IIIMP_ICState::message_proc()
   in case of IM_SETICFOCUS:
     start_request()->toggle_icfocus () [REQUESTED::toggle_focus ()]
       get_ichandler()->toggle_focus () [IMInputContext::toggle_focus ()]
         get_current_lecontext()->toggle_focus () [LEContext::toggle_focus ()]
           if_SetSCFocus () [SunIM.c: if_SetSCFocus ()]
             s->If->ifm->if_SetSCFocus () [specified LE's setfocus function]
       dealing ()
         execute remaining IML instructions (pushed by LEs)
         finish (), restore to previous state

* How does LE invoke server?
LE can not directly call server functions to send reply/messages to
client. LE calls iml methods (defined at iiimsf/lib/iml/SunIMSubr.c)
to create iml instructions, and append them to the operation list
(via iml_execute(), virtually is iml_execute_iml_wrapper() in LE.cpp).

As we mentioned early, IIIMP_ICState_REQUESTED::dealing() calls
IMLExec_ICState::execute() to process the pending instructions.

IMLExec_ICState::execute_opcode(), in turn calls imli_xxx methods,
these methods then call IIIMP_ICState_REQUESTED member functions which
actually send messages to client.

iml_make_commit_inst()/iml_execute()
               |
               | through an async flow
               V
in IMLExec_ICState::execute_opcode()
  case IMM_COMMIT:
    IMLExec_ICState::imli_commit ()
      IIIMP_ICState_REQUESTED::commit_string ()
        new an iiimp message
        send ()
          IIIMP_ICState::send ()
            IIIMP_IMState::send ()
              IIIMPTrans::send ()
                iiimf_stream_send ()
                  IIIMPTrans_write ()
                    IMSocketTrans::send () or
                    IMSocketTransTLS::send ()

* NameSpace I/O functions:
NameSpace I/O functions are passively invoked by LEs.

in each nsio function, there is a nested event dispatching loop.
Take open_ns () as an example:

   send_message = iiimp_open_ns_new (...);
   xims->send (send_message, true);

   for (;;) {
       xims->get_iiimptrans ()->receive ();
       if (opcode == IM_OPEN_NS_REPLY) {
           break;
       } else {
           xims->dispatch (...);
       }
   }

this loop is very simliar with IIIMProtocol::receive_and_dispatch.

* Misc:
ICState::send_avail_p ()
  IIIMP_ICState::state_send_available_p () { return false; }
  IIIMP_ICState_REQUESTED::state_send_available_p () { return true; }
  IIIMP_ICState_WAITING::state_send_available_p () { return false; }

-----------ICState::deliver ()--------------
if (!p->message_proc(message)) return false;
if (send_avail_p() && get_imlexec()) {
    result = get_imlexec()->execute();
}
--------------------------------------------

only when the icstate in requested, ICState::deliver () will try to
execute the buffered iml instructions. Because only in this state, the
server side (LEs) need to respond to client, and then LE may push iml
instructions in the queue.

Basicly, every branch in IIIMP_ICState::message_proc () will call
IIIMP_ICState_REQUESTED::dealing (), which actually calls
get_imlexec()->execute(). Is it a dup invoke in ICState::deliver ()?

X RECORD extension example

/*
 * To enable record extension in Xorg/XFree86, add the following  line in
 * Section "Module"
 *     Load         "record"
 */

#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlibint.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
#include <X11/keysymdef.h>
#include <X11/keysym.h>
#include <X11/extensions/record.h>
#include <X11/extensions/XTest.h>

/* for this struct, refer to libxnee */
typedef union {
  unsigned char    type ;
  xEvent           event ;
  xResourceReq     req   ;
  xGenericReply    reply ;
  xError           error ;
  xConnSetupPrefix setup;
} XRecordDatum;

/*
 * FIXME: We need define a private struct for callback function,
 * to store cur_x, cur_y, data_disp, ctrl_disp etc.
 */
Display *data_disp = NULL;
Display *ctrl_disp = NULL;

/* stop flag */
int stop = 0;

void event_callback (XPointer, XRecordInterceptData*);

int main (int argc, char **argv)
{
  ctrl_disp = XOpenDisplay (NULL);
  data_disp = XOpenDisplay (NULL);

  if (!ctrl_disp || !data_disp) {
    fprintf (stderr, "Error to open local display!\n");
    exit (1);
  }


  /*


   * we must set the ctrl_disp to sync mode, or, when we the enalbe


   * context in data_disp, there will be a fatal X error !!!


   */

  XSynchronize(ctrl_disp,True);

  int major, minor;
  if (!XRecordQueryVersion (ctrl_disp, &major, &minor)) {
    fprintf (stderr, "RECORD extension not supported on this X server!\n");
    exit (2);
  }
 
  printf ("RECORD extension for local server is version is %d.%d\n", major, minor);

  XRecordRange  *rr;
  XRecordClientSpec  rcs;
  XRecordContext   rc;

  rr = XRecordAllocRange ();
  if (!rr) {
    fprintf (stderr, "Could not alloc record range object!\n");
    exit (3);
  }

  rr->device_events.first = KeyPress;
  rr->device_events.last = MotionNotify;
  rcs = XRecordAllClients;

  rc = XRecordCreateContext (ctrl_disp, 0, &rcs, 1, &rr, 1);
  if (!rc) {
    fprintf (stderr, "Could not create a record context!\n");
    exit (4);
  }
 
  if (!XRecordEnableContextAsync (data_disp, rc, event_callback, NULL)) {
    fprintf (stderr, "Cound not enable the record context!\n");
    exit (5);
  }

  while (stop != 1) {
    XRecordProcessReplies (data_disp);
  }

  XRecordDisableContext (ctrl_disp, rc);
  XRecordFreeContext (ctrl_disp, rc);
  XFree (rr);
 
  XCloseDisplay (data_disp);
  XCloseDisplay (ctrl_disp);
  return 0;
}

void event_callback(XPointer priv, XRecordInterceptData *hook)
{
  /* FIXME: we need use XQueryPointer to get the first location */
  static int cur_x = 0;
  static int cur_y = 0;

  if (hook->category != XRecordFromServer) {
    XRecordFreeData (hook);
    return;
  }

  XRecordDatum *data = (XRecordDatum*) hook->data;

  int event_type = data->type;

  BYTE btncode, keycode;
  btncode = keycode = data->event.u.u.detail;

  int rootx = data->event.u.keyButtonPointer.rootX;
  int rooty = data->event.u.keyButtonPointer.rootY;
  int time = hook->server_time;

  switch (event_type) {
  case KeyPress:
    /* if escape is pressed, stop the loop and clean up, then exit */
    if (keycode == 9) stop = 1;

    /* Note: you should not use data_disp to do normal X operations !!!*/
    printf ("KeyPress: \t%s", XKeysymToString(XKeycodeToKeysym(ctrl_disp, keycode, 0)));
    break;
  case KeyRelease:
    printf ("KeyRelease: \t%s", XKeysymToString(XKeycodeToKeysym(ctrl_disp, keycode, 0)));
    break;
  case ButtonPress:
    printf ("ButtonPress: \t%d, rootX=%d, rootY=%d", btncode, cur_x, cur_y);
    break;
  case ButtonRelease:
    printf ("ButtonRelease: \t%d, rootX=%d, rootY=%d", btncode, cur_x, cur_y);
    break;
  case MotionNotify:
    printf ("MouseMove: \trootX=%d, rootY=%d",rootx, rooty);
    cur_x = rootx;
    cur_y = rooty;
    break;
  case CreateNotify:
    break;
  case DestroyNotify:
    break;
  case NoExpose:
    break;
  case Expose:
    break;
  default:
    break;
  }

  printf (", time=%d\n", time);

  XRecordFreeData (hook);
}