OpenCV: strange return value of cvWaitKey()

In OpenCV, cvWaitKey() might be one of most frequently used functions in interactive applications. For example (code in C++):

while(true)
{
    doSomething();

    if(cvWaitKey() == 27) // 27 is the key code of Escape
    {
        break;
    }
}

This works in Windows, but not work in Ubuntu. Both C++ version and python version can't catch escape key in Ubuntu. So annoying!

To find out what happened, I modify the code to print the return value of cvWaitKey(). The result for escape key is 1048603. Those who often coding might has a sense on this number - it's close to 1048576! In fact, it's 1048576+27. So strange! I google by using keyword "cvWaitKey 1048576", and found A Yahoo group question discussing this. Then I go to OpenCV on SourceForge and download the archived code. My downloaded version is 2.4.5. In modules/highgui/src/window_gtk.cpp, the body of cvWaitKey() is:

CV_IMPL int cvWaitKey( int delay )
{
#ifdef HAVE_GTHREAD
    if(thread_started && g_thread_self()!=window_thread){
        gboolean expired;
        int my_last_key;

        // wait for signal or timeout if delay > 0
        if(delay>0){
            GTimeVal timer;
            g_get_current_time(&timer);
            g_time_val_add(&timer, delay*1000);
            expired = !g_cond_timed_wait(cond_have_key, last_key_mutex, &timer);
        }
        else{
            g_cond_wait(cond_have_key, last_key_mutex);
            expired=false;
        }
        my_last_key = last_key;
        g_mutex_unlock(last_key_mutex);
        if(expired || hg_windows==0){
            return -1;
        }
        return my_last_key;
    }
    else{
#endif
        int expired = 0;
        guint timer = 0;
        if( delay > 0 )
            timer = g_timeout_add( delay, icvAlarm, &expired );
        last_key = -1;
        while( gtk_main_iteration_do(TRUE) && last_key < 0 && !expired && hg_windows != 0 )
            ;

        if( delay > 0 && !expired )
            g_source_remove(timer);
#ifdef HAVE_GTHREAD
    }
#endif
    return last_key;
}

Seems the returned value is not modified in this function. After simple search, I found it's set in icvOnKeyPress():

static gboolean icvOnKeyPress( GtkWidget * /*widget*/,
                GdkEventKey* event, gpointer /*user_data*/ )
{
    int code = 0;

    switch( event->keyval )
    {
    case GDK_Escape:
        code = 27;
        break;
    case GDK_Return:
    case GDK_Linefeed:
        code = '\n';
        break;
    case GDK_Tab:
        code = '\t';
    break;
    default:
        code = event->keyval;
    }

    code |= event->state << 16;

#ifdef HAVE_GTHREAD
    if(thread_started) g_mutex_lock(last_key_mutex);
#endif

    last_key = code;

#ifdef HAVE_GTHREAD
    if(thread_started){
        // signal any waiting threads
        g_cond_broadcast(cond_have_key);
        g_mutex_unlock(last_key_mutex);
    }
#endif

    return FALSE;
}

The consequence is clear: the returned value of cvWaitKey() is not exactly the key code. Practically, I use the code snippet below to detect keys:

while(true)
{
    doSomething();

    if(cvWaitKey() & 0xfffff == 27) // only pick 20 bits from LSB
    {
        break;
    }
}

This is what OpenCV in GTK+ happened. How about other platforms? I examined window_QT.cpp, window_w32.cpp (it's for windows), window_carbon.cpp, window_cocoa.mm (above two are for MacOSX), and all of them give exactly the key code from from underlying library in cvWaitKey(). I searched on the svn repository, and found that revision 617 is the first time window_gtk.cpp emerged. Then I searched in branch MACOSX_DEVELOPMENT. The first time window_gtk.cpp appeared is revision 508. Here the strange line exists, and from the commit log, I can find nothing.

Again, it's an example that cross-platform libraries behave differently on different platforms. But this time the condition is slightly different: the difference is caused by totally different codes, but not the programmer's insufficient sense on specific platforms.

social