From 26da491053d751e20032cafd764c483cc37d958f Mon Sep 17 00:00:00 2001 From: Christoph Rackwitz Date: Mon, 1 Feb 2021 15:44:03 +0100 Subject: [PATCH] implementation of pollKey, companion to waitKey w32 backend: implemented other backends: stubbed or fallback to waitKey documentation: cross-linked and more precise in some places --- modules/highgui/include/opencv2/highgui.hpp | 42 ++++-- modules/highgui/src/window.cpp | 2 + modules/highgui/src/window_QT.cpp | 7 + modules/highgui/src/window_cocoa.mm | 7 + modules/highgui/src/window_gtk.cpp | 7 + modules/highgui/src/window_w32.cpp | 151 ++++++++++++-------- modules/highgui/src/window_winrt.cpp | 8 ++ 7 files changed, 152 insertions(+), 72 deletions(-) diff --git a/modules/highgui/include/opencv2/highgui.hpp b/modules/highgui/include/opencv2/highgui.hpp index c5b5ed42c2..6b524f36a7 100644 --- a/modules/highgui/include/opencv2/highgui.hpp +++ b/modules/highgui/include/opencv2/highgui.hpp @@ -347,23 +347,34 @@ The function waitKey waits for a key event infinitely (when \f$\texttt{delay}\le milliseconds, when it is positive. Since the OS has a minimum time between switching threads, the function will not wait exactly delay ms, it will wait at least delay ms, depending on what else is running on your computer at that time. It returns the code of the pressed key or -1 if no key was -pressed before the specified time had elapsed. +pressed before the specified time had elapsed. To check for a key press but not wait for it, use +#pollKey. -@note - -This function is the only method in HighGUI that can fetch and handle events, so it needs to be -called periodically for normal event processing unless HighGUI is used within an environment that -takes care of event processing. +@note The functions #waitKey and #pollKey are the only methods in HighGUI that can fetch and handle +GUI events, so one of them needs to be called periodically for normal event processing unless +HighGUI is used within an environment that takes care of event processing. -@note - -The function only works if there is at least one HighGUI window created and the window is active. -If there are several HighGUI windows, any of them can be active. +@note The function only works if there is at least one HighGUI window created and the window is +active. If there are several HighGUI windows, any of them can be active. @param delay Delay in milliseconds. 0 is the special value that means "forever". */ CV_EXPORTS_W int waitKey(int delay = 0); +/** @brief Polls for a pressed key. + +The function pollKey polls for a key event without waiting. It returns the code of the pressed key +or -1 if no key was pressed since the last invocation. To wait until a key was pressed, use #waitKey. + +@note The functions #waitKey and #pollKey are the only methods in HighGUI that can fetch and handle +GUI events, so one of them needs to be called periodically for normal event processing unless +HighGUI is used within an environment that takes care of event processing. + +@note The function only works if there is at least one HighGUI window created and the window is +active. If there are several HighGUI windows, any of them can be active. + */ +CV_EXPORTS_W int pollKey(); + /** @brief Displays an image in the specified window. The function imshow displays an image in the specified window. If the window was created with the @@ -383,11 +394,12 @@ If the window was not created before this function, it is assumed creating a win If you need to show an image that is bigger than the screen resolution, you will need to call namedWindow("", WINDOW_NORMAL) before the imshow. -@note This function should be followed by cv::waitKey function which displays the image for specified -milliseconds. Otherwise, it won't display the image. For example, **waitKey(0)** will display the window -infinitely until any keypress (it is suitable for image display). **waitKey(25)** will display a frame -for 25 ms, after which display will be automatically closed. (If you put it in a loop to read -videos, it will display the video frame-by-frame) +@note This function should be followed by a call to cv::waitKey or cv::pollKey to perform GUI +housekeeping tasks that are necessary to actually show the given image and make the window respond +to mouse and keyboard events. Otherwise, it won't display the image and the window might lock up. +For example, **waitKey(0)** will display the window infinitely until any keypress (it is suitable +for image display). **waitKey(25)** will display a frame and wait approximately 25 ms for a key +press (suitable for displaying a video frame-by-frame). To remove the window, use cv::destroyWindow. @note diff --git a/modules/highgui/src/window.cpp b/modules/highgui/src/window.cpp index 8b26ea989b..05ac6b60ca 100644 --- a/modules/highgui/src/window.cpp +++ b/modules/highgui/src/window.cpp @@ -281,6 +281,8 @@ int cv::waitKey(int delay) return (code != -1) ? (code & 0xff) : -1; } +// NOTE: cv::pollKey has no C API equivalent. it is implemented in each backend source file. + int cv::createTrackbar(const String& trackbarName, const String& winName, int* value, int count, TrackbarCallback callback, void* userdata) diff --git a/modules/highgui/src/window_QT.cpp b/modules/highgui/src/window_QT.cpp index 1600bd917f..8d89ff0634 100644 --- a/modules/highgui/src/window_QT.cpp +++ b/modules/highgui/src/window_QT.cpp @@ -382,6 +382,13 @@ CV_IMPL int cvWaitKey(int delay) } +int cv::pollKey() +{ + CV_TRACE_FUNCTION(); + // fallback. please implement a proper polling function + return cvWaitKey(1); +} + //Yannick Verdie //This function is experimental and some functions (such as cvSet/getWindowProperty will not work) //We recommend not using this function for now diff --git a/modules/highgui/src/window_cocoa.mm b/modules/highgui/src/window_cocoa.mm index 29a0278c98..0933a6173a 100644 --- a/modules/highgui/src/window_cocoa.mm +++ b/modules/highgui/src/window_cocoa.mm @@ -632,6 +632,13 @@ CV_IMPL int cvWaitKey (int maxWait) return returnCode; } +int cv::pollKey() +{ + CV_TRACE_FUNCTION(); + // fallback. please implement a proper polling function + return cvWaitKey(1); +} + CvRect cvGetWindowRect_COCOA( const char* name ) { CvRect result = cvRect(-1, -1, -1, -1); diff --git a/modules/highgui/src/window_gtk.cpp b/modules/highgui/src/window_gtk.cpp index 073b340443..b2df50413e 100644 --- a/modules/highgui/src/window_gtk.cpp +++ b/modules/highgui/src/window_gtk.cpp @@ -1950,6 +1950,13 @@ CV_IMPL int cvWaitKey( int delay ) return last_key; } +int cv::pollKey() +{ + CV_TRACE_FUNCTION(); + // fallback. please implement a proper polling function + return cvWaitKey(1); +} + #endif // HAVE_GTK #endif // _WIN32 diff --git a/modules/highgui/src/window_w32.cpp b/modules/highgui/src/window_w32.cpp index 67fbc380e1..c565ad0fa8 100644 --- a/modules/highgui/src/window_w32.cpp +++ b/modules/highgui/src/window_w32.cpp @@ -1981,6 +1981,97 @@ static void showSaveDialog(CvWindow* window) } } +/* + * message received. check if it belongs to our windows (frame, hwnd). + * returns true (and value in keyCode) if a key was pressed. + * otherwise returns false (indication to continue event loop). + */ +static bool handleMessage(MSG& message, int& keyCode) +{ + // whether we have to call translate and dispatch yet + // otherwise the message was handled specifically + bool is_processed = false; + + for (CvWindow* window = hg_windows; window != 0 && is_processed == 0; window = window->next) + { + if (!(window->hwnd == message.hwnd || window->frame == message.hwnd)) + continue; + + is_processed = true; + switch (message.message) + { + case WM_DESTROY: + case WM_CHAR: + DispatchMessage(&message); + keyCode = (int)message.wParam; + return true; + + case WM_SYSKEYDOWN: + if (message.wParam == VK_F10) + { + is_processed = true; + keyCode = (int)(message.wParam << 16); + return true; + } + break; + + case WM_KEYDOWN: + TranslateMessage(&message); + if ((message.wParam >= VK_F1 && message.wParam <= VK_F24) || + message.wParam == VK_HOME || message.wParam == VK_END || + message.wParam == VK_UP || message.wParam == VK_DOWN || + message.wParam == VK_LEFT || message.wParam == VK_RIGHT || + message.wParam == VK_INSERT || message.wParam == VK_DELETE || + message.wParam == VK_PRIOR || message.wParam == VK_NEXT) + { + DispatchMessage(&message); + is_processed = true; + keyCode = (int)(message.wParam << 16); + return true; + } + + // Intercept Ctrl+C for copy to clipboard + if ('C' == message.wParam && (::GetKeyState(VK_CONTROL) >> 15)) + ::SendMessage(message.hwnd, WM_COPY, 0, 0); + + // Intercept Ctrl+S for "save as" dialog + if ('S' == message.wParam && (::GetKeyState(VK_CONTROL) >> 15)) + showSaveDialog(window); + + default: + DispatchMessage(&message); + is_processed = true; + break; + } + } + + if (!is_processed) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + + return false; // no value to return, keep processing +} + +/* + * process until queue is empty but don't wait. + */ +int cv::pollKey() +{ + CV_TRACE_FUNCTION(); + for(;;) + { + MSG message; + if (PeekMessage(&message, 0, 0, 0, PM_REMOVE) == FALSE) + return -1; + + int keyCode = -1; + if (handleMessage(message, keyCode)) + return keyCode; + } +} + CV_IMPL int cvWaitKey( int delay ) { @@ -1989,9 +2080,7 @@ cvWaitKey( int delay ) for(;;) { - CvWindow* window; MSG message; - int is_processed = 0; if( (delay <= 0) && hg_windows) GetMessage(&message, 0, 0, 0); @@ -2004,61 +2093,9 @@ cvWaitKey( int delay ) continue; } - for( window = hg_windows; window != 0 && is_processed == 0; window = window->next ) - { - if( window->hwnd == message.hwnd || window->frame == message.hwnd ) - { - is_processed = 1; - switch(message.message) - { - case WM_DESTROY: - case WM_CHAR: - DispatchMessage(&message); - return (int)message.wParam; - - case WM_SYSKEYDOWN: - if( message.wParam == VK_F10 ) - { - is_processed = 1; - return (int)(message.wParam << 16); - } - break; - - case WM_KEYDOWN: - TranslateMessage(&message); - if( (message.wParam >= VK_F1 && message.wParam <= VK_F24) || - message.wParam == VK_HOME || message.wParam == VK_END || - message.wParam == VK_UP || message.wParam == VK_DOWN || - message.wParam == VK_LEFT || message.wParam == VK_RIGHT || - message.wParam == VK_INSERT || message.wParam == VK_DELETE || - message.wParam == VK_PRIOR || message.wParam == VK_NEXT ) - { - DispatchMessage(&message); - is_processed = 1; - return (int)(message.wParam << 16); - } - - // Intercept Ctrl+C for copy to clipboard - if ('C' == message.wParam && (::GetKeyState(VK_CONTROL)>>15)) - ::SendMessage(message.hwnd, WM_COPY, 0, 0); - - // Intercept Ctrl+S for "save as" dialog - if ('S' == message.wParam && (::GetKeyState(VK_CONTROL)>>15)) - showSaveDialog(window); - - default: - DispatchMessage(&message); - is_processed = 1; - break; - } - } - } - - if( !is_processed ) - { - TranslateMessage(&message); - DispatchMessage(&message); - } + int keyCode = -1; + if (handleMessage(message, keyCode)) + return keyCode; } } diff --git a/modules/highgui/src/window_winrt.cpp b/modules/highgui/src/window_winrt.cpp index 1572929b90..ab2e761176 100644 --- a/modules/highgui/src/window_winrt.cpp +++ b/modules/highgui/src/window_winrt.cpp @@ -205,6 +205,14 @@ CV_IMPL int cvGetTrackbarPos(const char* trackbar_name, const char* window_name) /********************************** Not YET implemented API ****************************************************/ +int cv::pollKey() +{ + CV_TRACE_FUNCTION(); + CV_WINRT_NO_GUI_ERROR("cvPollKey"); + + // TODO: implement appropriate logic here +} + CV_IMPL int cvWaitKey(int delay) { CV_WINRT_NO_GUI_ERROR("cvWaitKey");