在交互式 GUI 代码中使用回调函数

Use of callback functions in interactive GUI code

提问人:Nitron_707 提问时间:7/9/2023 最后编辑:Eric PostpischilNitron_707 更新时间:7/9/2023 访问量:110

问:

我正在开发一个 GUI 系统并使用 https://github.com/stevenlovegrove/Pangolin,我使用回调函数来注册按键事件。

例如。

// register the function using the funcPtr
pangolin::RegisterKeyPressCallback(' ', funcPtr); 

while(!pangolin::ShouldQuit()){
  // ...some code...
  // Run till window is closed
  // ...some code...
}

这里基于穿山甲代码,它实现并使用了一个全局,它存储了函数指针,定义为RegisterKeyPressCallbackcontext

__thread PangolinGl* context = 0;

问题和场景 - 我们在回调函数中注册我们的 functionPtr,while 循环开始。 在我按的中间。 我想知道是否有后台线程始终在运行并侦听运行 while 循环的线程,以便当我按下暂停键时,它会检测到按键。RegisterKeyPressCallbackwhile looppause

一般来说,我想知道,在像上面这样的实时交互式用例中,我们传入一个指向回调函数的函数指针一次,并且是否有并行运行的后台线程,以便它可以检测事件。

C++ 回调

评论

0赞 Eric Postpischil 7/9/2023
不要为 C++ 问题或其他非 C 问题标记 C。

答:

3赞 Irelia 7/9/2023 #1

不,键盘输入没有单独的线程检查。相反,每个操作系统都有自己的处理窗口和键盘事件的实现。特别是在 Windows 上,代码必须注册一个窗口类,该类指示用于处理窗口消息事件的回调字段。查看 Windows 消息循环,其中窗口的每次迭代循环都会调用 GetMessage/PeekMessage 和 DispatchMessage 以进行事件处理。

在 Pangolin/components/pango_window/src/display_win.cpp 中,类和键盘回调函数在你之前注册,库的用户可以看到实际的窗口。

void WinWindow::RegisterThisClass(HMODULE hCurrentInst)
{
    WNDCLASSA wndClass;
    wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    wndClass.lpfnWndProc = WinWindow::WndProc;
    wndClass.cbClsExtra = 0;
    wndClass.cbWndExtra = 0;
    wndClass.hInstance = hCurrentInst;
    wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wndClass.lpszMenuName = NULL;
    wndClass.lpszClassName = className;
    if(!RegisterClassA(&wndClass)) {
        std::cerr << "RegisterClass() failed" << std::endl;
        CheckWGLDieOnError();
    }
}

函数 ProcessEvents 是实际消息循环发生的位置。

void WinWindow::ProcessEvents()
{
    static bool needs_init = true;
    if(needs_init) {
        RECT cRect;
        GetClientRect(hWnd, &cRect);
        ResizeSignal(WindowResizeEvent({cRect.right, cRect.bottom}));
        needs_init = false;
    }

    MSG msg;
    while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
        if (msg.message == WM_QUIT) {
            CloseSignal();
            break;
        }
        TranslateMessage(&msg);
        DispatchMessageA(&msg);
    }
}

具体来说,这是处理Windows消息的内容。WndProc 刚刚返回wndClass.lpfnWndProc = WinWindow::WndProc;LRESULT WinWindow::HandleWinMessages(UINT message, WPARAM wParam, LPARAM lParam)

WinWindow::HandleWinMessages然后检查窗口是否处理了键盘消息。如果是这样,库将执行检查以查看按下了哪个键,以及用户是否为该特定键注册了函数回调。并执行回调。

case WM_KEYDOWN:
    case WM_KEYUP:
    {
        unsigned char key = GetPangoKey(wParam, lParam);
        if(key > 0) {
            KeyboardSignal(KeyboardEvent({
                afLastMousePos[0], afLastMousePos[1],
                KeyModifierBitmask(),
                (unsigned char)key, message == WM_KEYDOWN
            }));
        }
        return 0;
    }

keyboardSignal(KeyboardEvent({是完成实际按键操作的地方。

请记住,我给出的概述是特定于 Windows 的。OSX、Unix、GNU都有自己的实现,我想穿山甲会逐案处理。

评论

0赞 Nitron_707 7/10/2023
所以我们在基本级别向操作系统注册函数,最后谁调用 funcPtr ??
1赞 Irelia 7/10/2023
@Nitron_707 在为该特定密钥注册自己的回调后,库会在 WM_KEYDOWN/WM_KEYUP 事件下执行。