
#include <windows.h> #include <optional> #include <iostream> #include <include/cef_app.h> #ifdef _DEBUG #pragma comment(lib, R"(D:\libraries\CEF\cef_binary_138.0.15+gd0f1f64+chromium-138.0.7204.50_windows64\Debug\libcef.lib)") #pragma comment(lib, R"(D:\libraries\CEF\MD\Debug\libcef_dll_wrapper.lib)") #else #pragma comment(lib, R"(D:\libraries\CEF\cef_binary_138.0.15+gd0f1f64+chromium-138.0.7204.50_windows64\Release\libcef.lib)") #pragma comment(lib, R"(D:\libraries\CEF\MD\Release\libcef_dll_wrapper.lib)") #endif #include "MyCEFControl.hpp" #include <Windows.h> #include <windowsx.h> #include <imm.h> #pragma comment(lib, "imm32.lib") void OnPaint(HWND hwnd, HDC hdc) { MyHandler* handler = (MyHandler*)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (handler) { CefRefPtr<CefRenderHandler> baseRenderHandler = handler->GetRenderHandler(); MyRenderHandler* renderHandler = static_cast<MyRenderHandler*>(baseRenderHandler.get()); RGBAImage img = renderHandler->m_browserImage; if (img.buffer.size() > 0) { // ビットマップ情報の設定 BITMAPINFO bmi; ZeroMemory(&bmi, sizeof(BITMAPINFO)); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = img.width; bmi.bmiHeader.biHeight = -img.height; // 上下反転 bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 24; // RGB24ビット bmi.bmiHeader.biCompression = BI_RGB; // 画像データを描画 SetDIBitsToDevice( hdc, 0, 0, // 描画位置 img.width, img.height, // 描画サイズ 0, 0, // ソースの開始位置 0, img.height, // ソースの開始ラインとライン数 img.buffer.data(), // ピクセルデータ &bmi, DIB_RGB_COLORS // カラーテーブルの使用方法 ); } } } void OnMouseMove(HWND hwnd, int x, int y) { MyHandler* handler = (MyHandler*)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (handler) { CefMouseEvent mouse_event; mouse_event.x = x; mouse_event.y = y; mouse_event.modifiers = 0; handler->GetBrowser()->GetHost()->SendMouseMoveEvent(mouse_event, false); InvalidateRect(hwnd, NULL, FALSE); } } void OnLButtonDown(HWND hwnd, int x, int y) { MyHandler* handler = (MyHandler*)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (handler) { CefMouseEvent mouse_event; mouse_event.x = x; mouse_event.y = y; mouse_event.modifiers = 0; handler->GetBrowser()->GetHost()->SendMouseClickEvent(mouse_event, cef_mouse_button_type_t::MBT_LEFT, false, 1); InvalidateRect(hwnd, NULL, FALSE); } } void OnLButtonUp(HWND hwnd, int x, int y) { MyHandler* handler = (MyHandler*)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (handler) { CefMouseEvent mouse_event; mouse_event.x = x; mouse_event.y = y; mouse_event.modifiers = 0; handler->GetBrowser()->GetHost()->SendMouseClickEvent(mouse_event, cef_mouse_button_type_t::MBT_LEFT, true, 1); InvalidateRect(hwnd, NULL, FALSE); } } void OnClose(HWND hwnd) { MyHandler* handler = (MyHandler*)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (handler) { handler->CloseAllBrowsers(true); } } std::optional<LRESULT> WndProc_IME_Work(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); void SendKeyEventToBrowser(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { MyHandler* handler = nullptr; LPCREATESTRUCT pcs; PAINTSTRUCT ps; HDC hdc; CefRefPtr<CefBrowser> browser; handler = reinterpret_cast<MyHandler*>(GetWindowLongPtr(hwnd, GWLP_USERDATA)); if(handler) browser = handler->GetBrowser(); SendKeyEventToBrowser(hwnd, msg, wp, lp);// キーボード入力のメッセージ処理 auto imeret = WndProc_IME_Work(hwnd, msg, wp, lp);// IME関係のメッセージ処理 if (imeret.has_value()) { return imeret.value(); } switch (msg) { case WM_CLOSE: OnClose(hwnd); DestroyWindow(hwnd); return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); OnPaint(hwnd, hdc); EndPaint(hwnd, &ps); return 0; case WM_CREATE: pcs = reinterpret_cast<LPCREATESTRUCT>(lp); SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pcs->lpCreateParams)); return 0; case WM_DESTROY: //CefQuitMessageLoop(); PostQuitMessage(0); return 0; case WM_MOUSEMOVE: OnMouseMove(hwnd, GET_X_LPARAM(lp), GET_Y_LPARAM(lp)); return 0; case WM_LBUTTONDOWN: if (handler && browser) browser->GetHost()->SetFocus(true); OnLButtonDown(hwnd, GET_X_LPARAM(lp), GET_Y_LPARAM(lp)); return 0; case WM_LBUTTONUP: OnLButtonUp(hwnd, GET_X_LPARAM(lp), GET_Y_LPARAM(lp)); return 0; } return DefWindowProc(hwnd, msg, wp, lp); } /////////////////////////////////////////////// /////////////////////////////////////////////// /////////////////////////////////////////////// void HandleImeComposition(HWND hWnd, CefRefPtr<CefBrowser> browser, UINT msg, WPARAM wParam, LPARAM lParam);
std::optional<LRESULT> WndProc_IME_Work(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { MyHandler* handler = (MyHandler*)GetWindowLongPtr(hWnd, GWLP_USERDATA); CefRefPtr<CefBrowser> browser = nullptr; if (handler) { browser = handler->GetBrowser(); } switch (msg) { case WM_SETFOCUS: if (browser) { browser->GetHost()->SetFocus(true); } break; case WM_KILLFOCUS: if (browser) { browser->GetHost()->SetFocus(false); } break; case WM_IME_STARTCOMPOSITION: return 0; case WM_IME_COMPOSITION: { HIMC hIMC = ImmGetContext(hWnd); if (hIMC) { POINT caret{}; CefRefPtr<CefRenderHandler> baseRenderHandler = handler->GetRenderHandler(); MyRenderHandler* renderHandler = static_cast<MyRenderHandler*>(baseRenderHandler.get()); // MyRenderHandler からキャレット位置を取得 if (renderHandler && renderHandler->GetImeCaretPos(caret)) { CANDIDATEFORM cf{}; cf.dwIndex = 0; cf.dwStyle = CFS_CANDIDATEPOS; cf.ptCurrentPos = caret; ImmSetCandidateWindow(hIMC, &cf); } } } if (browser) HandleImeComposition(hWnd, browser, msg, wParam, lParam); return 0; case WM_IME_ENDCOMPOSITION: if (browser) { browser->GetHost()->ImeFinishComposingText(true); } if (handler) { CefRefPtr<CefRenderHandler> baseRenderHandler = handler->GetRenderHandler(); MyRenderHandler* renderHandler = static_cast<MyRenderHandler*>(baseRenderHandler.get()); renderHandler->SetImeCaretInvalid(); } return 0; } return std::nullopt; }
void HandleImeComposition(HWND hWnd, CefRefPtr<CefBrowser> browser, UINT msg, WPARAM wParam, LPARAM lParam) { HIMC hIMC = ImmGetContext(hWnd); if (!hIMC) return; // 変換中文字列の取得 (GCS_COMPSTR) if (lParam & GCS_COMPSTR) { LONG size = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, nullptr, 0); std::wstring comp; comp.resize(size / sizeof(wchar_t)); ImmGetCompositionStringW(hIMC, GCS_COMPSTR, &comp[0], size); // キャレット位置 (GCS_CURSORPOS) LONG cursor = ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, nullptr, 0); // 属性(下線の種類などに使える) // LONG attrSize = ImmGetCompositionStringW(hIMC, GCS_COMPATTR, nullptr, 0); std::vector<CefCompositionUnderline> underlines; // 簡略のため全部同じ属性にしているが、 // 実際は GCS_COMPATTR の値から変換中部分/確定部分などを判定して設定する CefCompositionUnderline ul; ul.range = CefRange(0, static_cast<int>(comp.size())); ul.color = 0; // デフォルト色 ul.background_color = 0; // デフォルト背景 ul.thick = false; underlines.push_back(ul); // selection_range, replacement_range はここでは簡略化 CefRange selection(cursor, cursor); browser->GetHost()->ImeSetComposition( comp, underlines, CefRange::InvalidRange(), // replacement_range selection // selection_range ); } // 確定文字列の取得 (GCS_RESULTSTR) if (lParam & GCS_RESULTSTR) { LONG size = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, nullptr, 0); std::wstring result; result.resize(size / sizeof(wchar_t)); ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, &result[0], size); // resultをDebugString OutputDebugStringW(result.c_str()); browser->GetHost()->ImeCommitText( result, CefRange::InvalidRange(), 0 // relative_cursor_pos ); browser->GetHost()->ImeFinishComposingText(false); } ImmReleaseContext(hWnd, hIMC); }
/////////////////////////////////////////////// /////////////////////////////////////////////// ///////////////////////////////////////////////
void SendKeyEventToBrowser(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { MyHandler* handler = reinterpret_cast<MyHandler*>(GetWindowLongPtr(hWnd, GWLP_USERDATA)); if (!handler) return; CefRefPtr<CefBrowser> browser = handler->GetBrowser(); if (!browser) return; CefKeyEvent ev; switch (msg) { case WM_KEYDOWN: case WM_SYSKEYDOWN: ev.type = KEYEVENT_RAWKEYDOWN; break; case WM_KEYUP: case WM_SYSKEYUP: ev.type = KEYEVENT_KEYUP; break; case WM_CHAR: case WM_SYSCHAR: ev.type = KEYEVENT_CHAR; break; default: return; // 対象外 } ev.windows_key_code = static_cast<int>(wParam); ev.native_key_code = static_cast<int>(lParam); ev.is_system_key = (msg == WM_SYSKEYDOWN || msg == WM_SYSKEYUP || msg == WM_SYSCHAR); // 必要なら修飾キーも付与 ev.modifiers = 0; if (GetKeyState(VK_SHIFT) & 0x8000) ev.modifiers |= EVENTFLAG_SHIFT_DOWN; if (GetKeyState(VK_CONTROL) & 0x8000) ev.modifiers |= EVENTFLAG_CONTROL_DOWN; if (GetKeyState(VK_MENU) & 0x8000) ev.modifiers |= EVENTFLAG_ALT_DOWN; browser->GetHost()->SendKeyEvent(ev); }
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { HWND hwnd; MSG msg; WNDCLASS winc; winc.style = CS_HREDRAW | CS_VREDRAW; winc.lpfnWndProc = WndProc; winc.cbClsExtra = winc.cbWndExtra = 0; winc.hInstance = hInstance; winc.hIcon = LoadIcon(NULL, IDI_APPLICATION); winc.hCursor = LoadCursor(NULL, IDC_ARROW); winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); winc.lpszMenuName = NULL; winc.lpszClassName = TEXT("SZL-WND"); if (!RegisterClass(&winc)) return -1; ///////////////////////////////////////////////////////////////////////// CefRefPtr<MyHandler> g_handler = MyInitCEF(hInstance); if (!g_handler) { return -1; } ///////////////////////////////////////////////////////////////////////// hwnd = CreateWindow( TEXT("SZL-WND"), TEXT("CEF test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 500, 500, NULL, NULL, hInstance, g_handler.get() ); if (hwnd == NULL) return -1; ///////////////////////////////////////////////////////////////////////// //SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)(g_handler.get())); ///////////////////////////////////////////////////////////////////////// MyRenderSetting(hwnd, g_handler); ///////////////////////////////////////////////////////////////////////// // メッセージループ CefRunMessageLoop(); // CEFのシャットダウン CefShutdown(); return 0; }
#pragma once #include <include/cef_app.h> #include <optional> struct RGBAImage { int width; int height; std::vector<unsigned char> buffer; // RGBAの生データ }; RGBAImage FromRGBA(const unsigned char* rgba_buffer, int width, int height); class MyRenderHandler : public CefRenderHandler { public: HWND m_pCanvasWindow = nullptr; RGBAImage m_browserImage; HWND m_main_hwnd = nullptr; // IME用にメインウィンドウのハンドルを保持 // 追加: 最後に通知されたキャレット矩形(ブラウザの View 座標系) std::optional<CefRect> m_ime_caret_rect; // GetViewRect() をoverrideする // 必須:ビューポートのサイズを返す void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) override; // 必須:レンダリング結果がここに来る void OnPaint(CefRefPtr<CefBrowser> browser, PaintElementType type, const RectList& dirtyRects, const void* buffer, int width, int height) override; // サイズを外から設定 void SetSize(int w, int h); // メインウィンドウのハンドルを設定 void SetMainHwnd(HWND hwnd); // IMEのキャレット位置が変化したときに呼ばれる void OnImeCompositionRangeChanged( CefRefPtr<CefBrowser> browser, const CefRange& selected_range, const CefRenderHandler::RectList& character_bounds) override; bool GetImeCaretPos(POINT& pt) const; void SetImeCaretInvalid(); private: IMPLEMENT_REFCOUNTING(MyRenderHandler); }; class MyHandler : public CefClient, public CefLifeSpanHandler, public CefRenderHandler { CefRefPtr<CefRenderHandler> renderHandler_; public: MyHandler(CefRefPtr<CefRenderHandler> renderHandler) : renderHandler_(renderHandler) { } CefRefPtr<CefRenderHandler> GetRenderHandler() override; CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override; void OnAfterCreated(CefRefPtr<CefBrowser> browser) override; void OnBeforeClose(CefRefPtr<CefBrowser> browser) override; void CloseAllBrowsers(bool force_close); CefRefPtr<CefBrowser> GetBrowser(); void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) override { return renderHandler_->GetViewRect(browser, rect); } void OnPaint(CefRefPtr<CefBrowser> browser, PaintElementType type, const RectList& dirtyRects, const void* buffer, int width, int height) override { return renderHandler_->OnPaint(browser, type, dirtyRects, buffer, width, height); } IMPLEMENT_REFCOUNTING(MyHandler); private: CefRefPtr<CefBrowser> m_Browser; }; CefRefPtr<MyHandler> MyInitCEF(HINSTANCE hInstance); void MyRenderSetting(HWND window, CefRefPtr<MyHandler> ghandler);
#include "MyCEFControl.hpp" void MyRenderHandler::GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) { // // ビューポートのサイズを返す rect = CefRect(0, 0, m_browserImage.width, m_browserImage.height); } // 必須:レンダリング結果がここに来る void MyRenderHandler::OnPaint(CefRefPtr<CefBrowser> browser, PaintElementType type, const RectList& dirtyRects, const void* buffer, int width, int height) { // ※ buffer は 32bit BGRA フォーマット m_browserImage = FromRGBA(static_cast<const unsigned char*>(buffer), width, height); InvalidateRect(m_pCanvasWindow, NULL, FALSE); } // サイズを外から設定 void MyRenderHandler::SetSize(int w, int h) { m_browserImage.buffer.resize(w * h * 4); m_browserImage.width = w; m_browserImage.height = h; } void MyRenderHandler::OnImeCompositionRangeChanged( CefRefPtr<CefBrowser> browser, const CefRange& selected_range, const CefRenderHandler::RectList& character_bounds) { // character_bounds[0] がキャレット位置に対応する矩形 if (character_bounds.empty()) return; /////////////////////////////////////////////////////////// #if 0 int idx = 0; // 選択範囲の末尾をキャレットとみなす if (selected_range.to > 0 && selected_range.to <= static_cast<int>(character_bounds.size())) { idx = selected_range.to - 1; } else { idx = static_cast<int>(character_bounds.size()) - 1; } m_ime_caret_rect = character_bounds[idx]; #else // 常に最初の矩形をキャレット位置とみなす m_ime_caret_rect = character_bounds[0]; #endif } bool MyRenderHandler::GetImeCaretPos(POINT& pt) const { if (!m_ime_caret_rect) return false; pt.x = m_ime_caret_rect->x; pt.y = m_ime_caret_rect->y; return true; } void MyRenderHandler::SetImeCaretInvalid() { m_ime_caret_rect = std::nullopt; } void MyRenderHandler::SetMainHwnd(HWND hwnd) { m_main_hwnd = hwnd; } //============================================================= CefRefPtr<CefRenderHandler> MyHandler::GetRenderHandler() { return renderHandler_; } CefRefPtr<CefLifeSpanHandler> MyHandler::GetLifeSpanHandler() { return this; } void MyHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) { m_Browser = browser; } void MyHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) { m_Browser = nullptr; CefQuitMessageLoop(); } void MyHandler::CloseAllBrowsers(bool force_close) { if (m_Browser) { m_Browser->GetHost()->CloseBrowser(force_close); } } CefRefPtr<CefBrowser> MyHandler::GetBrowser() { return m_Browser; } //============================================================= RGBAImage 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 + 0]; rgb_data[i * 3 + 1] = rgba_buffer[i * 4 + 1]; rgb_data[i * 3 + 2] = rgba_buffer[i * 4 + 2]; } RGBAImage img; img.width = width; img.height = height; img.buffer = std::move(rgb_data); return img; } CefRefPtr<MyHandler> MyInitCEF(HINSTANCE hInstance) { CefMainArgs main_args(hInstance); int exit_code = CefExecuteProcess(main_args, nullptr, nullptr); if (exit_code >= 0) return nullptr; // CEFの設定 CefSettings settings; settings.no_sandbox = true; // マルチスレッドメッセージループを無効にする // オフスクリーンレンダリングでは false必須 settings.multi_threaded_message_loop = false; CefInitialize(main_args, settings, nullptr, nullptr); ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// CefRefPtr<MyRenderHandler> renderHandler = new MyRenderHandler(); CefRefPtr<MyHandler> g_handler = CefRefPtr<MyHandler>(new MyHandler(renderHandler)); return g_handler; } void MyRenderSetting(HWND window, CefRefPtr<MyHandler> ghandler) { // MyRenderHandlerを取得 CefRefPtr<CefRenderHandler> baseRenderHandler = ghandler->GetRenderHandler(); CefRefPtr<MyRenderHandler> renderHandler = static_cast<MyRenderHandler*>(baseRenderHandler.get()); renderHandler->m_pCanvasWindow = window; renderHandler->SetSize(400, 400); // ビューポートのサイズを設定 renderHandler->SetMainHwnd(window); // IME用にメインウィンドウハンドルを設定 CefBrowserSettings browser_settings; CefWindowInfo window_info; CefRect cefRect( 0, 0, 800, 600); // オフスクリーンレンダリング window_info.SetAsWindowless(nullptr); CefBrowserHost::CreateBrowser( window_info, ghandler, "https://www.google.com", browser_settings, nullptr, nullptr); }