mpelec使用一个透明窗体覆盖在libmpv渲染的窗体上。最初实现绑定两个窗体位置的方法是基于Electron上窗口的两个事件:

osc.on("move", () => {
    win.setBounds(osc.getBounds());
});
osc.on("resize", () => {
    win.setBounds(osc.getBounds());
});

这样处理确实OK,但是有性能上的问题:拖动的时候窗口绘制卡顿,然后风扇开始发力...这张是使用Electron事件处理的方法,移动的时候窗口错位、帧率较低。

为了提高体验,我考虑到使用更底层的方法来绑定窗体:Hook原本的WindowProc函数,手动处理WM_SIZE和WM_MOVE两个消息。

使用到的关键函数是 SetWindowLongPtrGetWindowLongPtr ,前者用来注入我们自定义的消息处理函数,后者用来保存原本的函数以供处理其他消息。

获取窗体指针并绑定自定义函数

js端的调用:

let win = new electron.BrowserWindow({
    ...WindowConfig.PWIN
});
let hwnd_pwin = win.getNativeWindowHandle().readInt32LE();

let osc = new electron.BrowserWindow({
    ...WindowConfig.OSC,
    parent: win
});
let hwnd_osc = osc.getNativeWindowHandle().readInt32LE();

// bind window movement
addon.bind_window(hwnd_osc, hwnd_pwin);

在C++ Addon的被调用函数中:

Napi::Value BindMove(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    if (info.Length() < 2)
    {
        Napi::TypeError::New(env, "Need more args.").ThrowAsJavaScriptException();
        return env.Null();
    }
    HWND osc_hwnd = (HWND)(info[0].As<Napi::Number>().Int32Value());
    HWND pwin_hwnd = (HWND)(info[1].As<Napi::Number>().Int32Value());
    osc = osc_hwnd;
    pwin = pwin_hwnd;
    old_proc = (WNDPROC)GetWindowLongPtr(pwin, GWLP_WNDPROC);
    SetWindowLongPtr(osc_hwnd, GWLP_WNDPROC, (LONG_PTR)_HookWindowProc);
    return env.Null();
}

实际处理消息的_HookWindowProc函数:

LRESULT CALLBACK _HookWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if (uMsg == WM_MOVE || uMsg == WM_SIZE)
    {
        RECT osc_rect;
        GetWindowRect(osc, &osc_rect);
        SetWindowPos(pwin,
                     HWND_TOPMOST,
                     osc_rect.left,
                     osc_rect.top,
                     osc_rect.right - osc_rect.left,
                     osc_rect.bottom - osc_rect.top,
                     SWP_NOZORDER);
    }
    return CallWindowProc(old_proc, hwnd, uMsg, wParam, lParam);
}

效果对比

折腾这么多,实际效果如何? 努力没白费,使用底层方法处理后,窗体移动丝滑流畅。两段比较的视频不知道怎么上传,转成GIF对比起来又不明显,日后有机会再上视频比较吧。