スポンサーリンク
・settings.multi_threaded_message_loop = false; にする
・CefRenderHandler をオーバーライドしCefClientのインスタンスに設定
・イベントはSendMouseClickEvent等で送信
#ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 #ifdef _DEBUG #pragma comment(lib, "D:\\cmmon\\cef_binary_138.0.15+gd0f1f64+chromium-138.0.7204.50_windows64\\Debug\\libcef.lib") #pragma comment(lib, "D:\\cmmon\\MD\\Debug\\libcef_dll_wrapper.lib") #else #pragma comment(lib, "D:\\cmmon\\cef_binary_138.0.15+gd0f1f64+chromium-138.0.7204.50_windows64\\Release\\libcef.lib") #pragma comment(lib, "D:\\cmmon\\MD\\Release\\libcef_dll_wrapper.lib") #endif #include <wx/evtloop.h> ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// #include <include/cef_app.h> #include <string> wxBitmap FromRGBA(const unsigned char* rgba_buffer, int width, int height) { // wxImage用にRGBを抜き出す // 既存の生配列からstd::vectorへの変換例 std::vector<unsigned char> rgb_data(rgba_buffer, rgba_buffer + width * height * 3); for (int i = 0; i < width * height; ++i) { rgb_data[i * 3 + 0] = rgba_buffer[i * 4 + 2]; rgb_data[i * 3 + 1] = rgba_buffer[i * 4 + 1]; rgb_data[i * 3 + 2] = rgba_buffer[i * 4 + 0]; } wxImage image(width, height, rgb_data.data(), true); // wxBitmapへ変換 return wxBitmap(image); }
class MyRenderHandler : public CefRenderHandler { public: wxFrame* m_pFrame = nullptr; wxBitmap *m_pbitmap = nullptr; void setBitmap(wxBitmap *bmp) { m_pbitmap = bmp; } void setFrame(wxFrame* frame) { m_pFrame = frame; } // GetViewRect() をoverrideする // 必須:ビューポートのサイズを返す void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) override { // ビューポートのサイズを返す rect = CefRect(0, 0, width_, height_); } // 必須:レンダリング結果がここに来る void OnPaint(CefRefPtr<CefBrowser> browser, PaintElementType type, const RectList& dirtyRects, const void* buffer, int width, int height) override { // ※ buffer は 32bit BGRA フォーマット *m_pbitmap = FromRGBA(static_cast<const unsigned char*>(buffer), width, height); if (m_pFrame) { m_pFrame->Refresh(); // ウィンドウを更新 } } // 例: サイズを外から設定 void SetSize(int w, int h) { width_ = w; height_ = h; } private: int width_ = 800; int height_ = 600; IMPLEMENT_REFCOUNTING(MyRenderHandler); };
class MyHandler : public CefClient, public CefLifeSpanHandler { // CefRefPtr<CefRenderHandler> renderHandler_; public: // MyHandler(CefRefPtr<CefRenderHandler> renderHandler) : renderHandler_(renderHandler) {} // CefRefPtr<CefRenderHandler> GetRenderHandler() override { return renderHandler_; } CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; } void OnAfterCreated(CefRefPtr<CefBrowser> browser) override { m_Browser = browser; } void OnBeforeClose(CefRefPtr<CefBrowser> browser) override { m_Browser = nullptr; // イベントから抜ける if (wxEventLoopBase::GetActive()) { wxEventLoopBase::GetActive()->Exit(); } } void CloseAllBrowsers(bool force_close) { if (m_Browser) { m_Browser->GetHost()->CloseBrowser(force_close); } } CefRefPtr<CefBrowser> GetBrowser() { return m_Browser; } IMPLEMENT_REFCOUNTING(MyHandler); private: CefRefPtr<CefBrowser> m_Browser; };
class MyCustomEventLoop : public wxGUIEventLoop { public: MyCustomEventLoop() = default; virtual ~MyCustomEventLoop() = default; protected: // Yieldが必要な場合 virtual void DoYieldFor(long eventsToProcess) override { // デフォルトの実装 wxGUIEventLoop::DoYieldFor(eventsToProcess); } // メッセージループの各イテレーション開始時に呼ばれるフック virtual void OnNextIteration() override { // デフォルトの実装 wxGUIEventLoop::OnNextIteration(); } ////////////////////////////////////////////// // メッセージを処理する bool Dispatch() override { bool running = true; MSG msg; while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) running = false; else { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } } CefDoMessageLoopWork(); ::Sleep(10); return true; // まだ継続 } ////////////////////////////////////////////// };
// メインフレーム class MyFrame : public wxFrame { CefRefPtr<MyHandler> g_handler; wxBitmap m_cefbmp; public: MyFrame() : wxFrame(nullptr, wxID_ANY, "CEF Offscreen Rendering") { Bind(wxEVT_CLOSE_WINDOW, &MyFrame::OnClose, this); Bind(wxEVT_SIZE, &MyFrame::OnSize, this); Bind(wxEVT_PAINT, &MyFrame::OnPaint, this); Bind(wxEVT_LEFT_DOWN, &MyFrame::OnLButtonDown, this); Bind(wxEVT_LEFT_UP, &MyFrame::OnLButtonUp, this); CallAfter(&MyFrame::PostCreate); } private:
void OnLButtonDown(wxMouseEvent& event) { CefMouseEvent mouse_event; mouse_event.x = event.GetX(); mouse_event.y = event.GetY(); mouse_event.modifiers = event.GetModifiers(); // マウスボタンDOWN g_handler->GetBrowser()->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, false, // isMouseUp == false (DOWN) 1 ); event.Skip(); // 必要なら他のハンドラにも渡す }
void OnLButtonUp(wxMouseEvent& event) { CefMouseEvent mouse_event; mouse_event.x = event.GetX(); mouse_event.y = event.GetY(); mouse_event.modifiers = event.GetModifiers(); // マウスボタンUP g_handler->GetBrowser()->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, true, // isMouseUp == true (UP) 1 ); event.Skip(); }
void PostCreate() { CefRefPtr<MyRenderHandler> renderHandler = new MyRenderHandler(); renderHandler->setBitmap(&m_cefbmp); renderHandler->setFrame(this); renderHandler->SetSize(400, 400); // ビューポートのサイズを設定 g_handler = CefRefPtr<MyHandler>(new MyHandler(renderHandler) ); CefBrowserSettings browser_settings; CefWindowInfo window_info; CefRect cefRect( 0, 0, 400, 400); HWND hwnd = (HWND)this->GetHandle(); //window_info.SetAsChild(hwnd, cefRect); // オフスクリーンレンダリング window_info.SetAsWindowless(nullptr); CefBrowserHost::CreateBrowser( window_info, g_handler, "https://www.google.com", browser_settings, nullptr, nullptr); this->Layout(); // レイアウトの更新 } void OnClose(wxCloseEvent& evt) { // CEFのブラウザを閉じる g_handler->CloseAllBrowsers(true); // ウィンドウを閉じない指示 evt.Veto(); } void OnSize(wxSizeEvent& evt) { if (g_handler) { HWND hBrowserWnd = g_handler->GetBrowser()->GetHost()->GetWindowHandle(); // ウィンドウサイズ変更時の処理 if (g_handler && g_handler->GetBrowser()) { RECT rect(0, 0, evt.GetSize().GetWidth(), evt.GetSize().GetHeight()); SetWindowPos(hBrowserWnd, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER); // ブラウザにサイズ変更を通知 g_handler->GetBrowser()->GetHost()->WasResized(); } } evt.Skip(); // イベントをスキップしてデフォルトの処理を行う } // OnPaint() void OnPaint(wxPaintEvent& evt) { // 描画処理をここに追加 wxPaintDC dc(this); if (m_cefbmp.IsOk()) { // CEFのビットマップを描画 dc.DrawBitmap(m_cefbmp, 0, 0, true); } } }; class MyApp : public wxApp { public: bool OnInit() override { HINSTANCE hInstance = ::GetModuleHandle(NULL); CefMainArgs main_args(hInstance); int exit_code = CefExecuteProcess(main_args, nullptr, nullptr); if (exit_code >= 0) return false; // CEFの設定 CefSettings settings; settings.no_sandbox = true; // マルチスレッドメッセージループを無効にする // オフスクリーンレンダリングでは false必須 settings.multi_threaded_message_loop = false; CefInitialize(main_args, settings, nullptr, nullptr); /////////////////////////////////////////////////// auto frame = new MyFrame(); frame->Show(); return true; } int OnRun() override { // wxGUIEventLoop を使って自前ループを構築 MyCustomEventLoop* loop = new MyCustomEventLoop; wxEventLoopBase::SetActive(loop); // アクティブなループとして登録 int retval = loop->Run(); // ここでメッセージループ開始 delete loop; // ループ終了後に解放 // CEFのシャットダウン CefShutdown(); return retval; } virtual int OnExit() { return wxApp::OnExit(); } }; // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);