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.