Last active
September 25, 2023 15:08
-
-
Save LinArcX/0e9653bb4b9d7ef346adcc9b50282114 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <iostream> | |
#include <sstream> | |
#include <SDL.h> | |
#include <include/cef_app.h> | |
#include <include/cef_client.h> | |
#include <include/cef_render_handler.h> | |
#include <include/cef_life_span_handler.h> | |
#include <include/cef_load_handler.h> | |
#include <include/wrapper/cef_helpers.h> | |
#include "sdl_keyboard_utils.h" | |
class RenderHandler : | |
public CefRenderHandler | |
{ | |
public: | |
RenderHandler(SDL_Renderer* renderer, int w, int h) : | |
width(w), | |
height(h), | |
renderer(renderer) | |
{ | |
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_UNKNOWN, SDL_TEXTUREACCESS_STREAMING, w, h); | |
} | |
~RenderHandler() | |
{ | |
if (texture) | |
{ | |
SDL_DestroyTexture(texture); | |
} | |
renderer = nullptr; | |
} | |
void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) | |
{ | |
rect = CefRect(0, 0, width, height); | |
//return true; | |
} | |
void OnPaint(CefRefPtr<CefBrowser> browser, PaintElementType type, const RectList& dirtyRects, const void* buffer, int w, int h) | |
{ | |
if (texture) | |
{ | |
unsigned char* texture_data = NULL; | |
int texture_pitch = 0; | |
SDL_LockTexture(texture, 0, (void**)&texture_data, &texture_pitch); | |
memcpy(texture_data, buffer, w * h * 4); | |
SDL_UnlockTexture(texture); | |
} | |
} | |
void resize(int w, int h) | |
{ | |
if (texture) | |
{ | |
SDL_DestroyTexture(texture); | |
} | |
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_UNKNOWN, SDL_TEXTUREACCESS_STREAMING, w, h); | |
width = w; | |
height = h; | |
} | |
void render() | |
{ | |
SDL_RenderCopy(renderer, texture, NULL, NULL); | |
} | |
private: | |
int width; | |
int height; | |
SDL_Renderer* renderer = nullptr; | |
SDL_Texture* texture = nullptr; | |
IMPLEMENT_REFCOUNTING(RenderHandler); | |
}; | |
// for manual render handler | |
class BrowserClient : | |
public CefClient, | |
public CefLifeSpanHandler, | |
public CefLoadHandler | |
{ | |
public: | |
BrowserClient(CefRefPtr<CefRenderHandler> ptr) : | |
handler(ptr) | |
{ | |
} | |
virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() | |
{ | |
return this; | |
} | |
virtual CefRefPtr<CefLoadHandler> GetLoadHandler() | |
{ | |
return this; | |
} | |
virtual CefRefPtr<CefRenderHandler> GetRenderHandler() | |
{ | |
return handler; | |
} | |
// CefLifeSpanHandler methods. | |
void OnAfterCreated(CefRefPtr<CefBrowser> browser) | |
{ | |
// Must be executed on the UI thread. | |
CEF_REQUIRE_UI_THREAD(); | |
browser_id = browser->GetIdentifier(); | |
} | |
bool DoClose(CefRefPtr<CefBrowser> browser) | |
{ | |
// Must be executed on the UI thread. | |
CEF_REQUIRE_UI_THREAD(); | |
// Closing the main window requires special handling. See the DoClose() | |
// documentation in the CEF header for a detailed description of this | |
// process. | |
if (browser->GetIdentifier() == browser_id) | |
{ | |
// Set a flag to indicate that the window close should be allowed. | |
closing = true; | |
} | |
// Allow the close. For windowed browsers this will result in the OS close | |
// event being sent. | |
return false; | |
} | |
void OnBeforeClose(CefRefPtr<CefBrowser> browser) | |
{ | |
} | |
void OnLoadEnd(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, int httpStatusCode) | |
{ | |
std::cout << "OnLoadEnd(" << httpStatusCode << ")" << std::endl; | |
loaded = true; | |
} | |
bool OnLoadError(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefLoadHandler::ErrorCode errorCode, const CefString& failedUrl, CefString& errorText) | |
{ | |
std::cout << "OnLoadError()" << std::endl; | |
loaded = true; | |
} | |
void OnLoadingStateChange(CefRefPtr<CefBrowser> browser, bool isLoading, bool canGoBack, bool canGoForward) | |
{ | |
std::cout << "OnLoadingStateChange()" << std::endl; | |
} | |
void OnLoadStart(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame) | |
{ | |
std::cout << "OnLoadStart()" << std::endl; | |
} | |
bool closeAllowed() const | |
{ | |
return closing; | |
} | |
bool isLoaded() const | |
{ | |
return loaded; | |
} | |
private: | |
int browser_id; | |
bool closing = false; | |
bool loaded = false; | |
CefRefPtr<CefRenderHandler> handler; | |
IMPLEMENT_REFCOUNTING(BrowserClient); | |
}; | |
CefBrowserHost::MouseButtonType translateMouseButton(SDL_MouseButtonEvent const& e) | |
{ | |
CefBrowserHost::MouseButtonType result; | |
switch (e.button) | |
{ | |
case SDL_BUTTON_LEFT: | |
case SDL_BUTTON_X1: | |
result = MBT_LEFT; | |
break; | |
case SDL_BUTTON_MIDDLE: | |
result = MBT_MIDDLE; | |
break; | |
case SDL_BUTTON_RIGHT: | |
case SDL_BUTTON_X2: | |
result = MBT_RIGHT; | |
break; | |
} | |
return result; | |
} | |
int main(int argc, char* argv[]) | |
{ | |
CefMainArgs args; | |
{ | |
int result = CefExecuteProcess(args, nullptr, nullptr); | |
// checkout CefApp, derive it and set it as second parameter, for more control on | |
// command args and resources. | |
if (result >= 0) // child proccess has endend, so exit. | |
{ | |
return result; | |
} | |
else if (result == -1) | |
{ | |
// we are here in the father proccess. | |
} | |
} | |
{ | |
CefSettings settings; | |
// CefString(&settings.resources_dir_path).FromASCII(""); | |
{ | |
std::ostringstream ss; | |
ss << SDL_GetBasePath() << "locales/"; | |
CefString(&settings.locales_dir_path) = ss.str(); | |
} | |
// CefString(&settings.locales_dir_path).FromASCII(""); | |
CefString(&settings.resources_dir_path) = SDL_GetBasePath(); | |
// checkout detailed settings options http://magpcss.org/ceforum/apidocs/projects/%28default%29/_cef_settings_t.html | |
// nearly all the settings can be set via args too. | |
// settings.multi_threaded_message_loop = true; // not supported, except windows | |
// CefString(&settings.browser_subprocess_path).FromASCII("sub_proccess path, by default uses and starts this executeable as child"); | |
// CefString(&settings.cache_path).FromASCII(""); | |
// CefString(&settings.log_file).FromASCII(""); | |
// settings.log_severity = LOGSEVERITY_DEFAULT; | |
bool result = CefInitialize(args, settings, nullptr, nullptr); | |
// CefInitialize creates a sub-proccess and executes the same executeable, as calling CefInitialize, if not set different in settings.browser_subprocess_path | |
// if you create an extra program just for the childproccess you only have to call CefExecuteProcess(...) in it. | |
if (!result) | |
{ | |
// handle error | |
return -1; | |
} | |
} | |
//Initialize SDL | |
if (SDL_Init(SDL_INIT_VIDEO) < 0) | |
{ | |
std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl; | |
return 1; | |
} | |
int width = 800; | |
int height = 600; | |
// // Run the CEF message loop. This will block until CefQuitMessageLoop() is | |
// // called. | |
//CefRunMessageLoop(); | |
auto window = SDL_CreateWindow("Render CEF with SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_RESIZABLE); | |
if (window) | |
{ | |
auto renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); | |
if (renderer) | |
{ | |
SDL_Event e; | |
CefRefPtr<RenderHandler> renderHandler = new RenderHandler(renderer, width, height); | |
// create browser-window | |
CefRefPtr<CefBrowser> browser; | |
CefRefPtr<BrowserClient> browserClient; | |
{ | |
CefWindowInfo window_info; | |
CefBrowserSettings browserSettings; | |
// browserSettings.windowless_frame_rate = 60; // 30 is default | |
//window_info.SetAsWindowless(NULL, false); // false means no transparency (site background colour) | |
browserClient = new BrowserClient(renderHandler); | |
browser = CefBrowserHost::CreateBrowserSync(window_info, browserClient.get(), "http://www.google.com", browserSettings, nullptr,nullptr); | |
// inject user-input by calling - non-trivial for non-windows - checkout the cefclient source and the platform specific cpp, like cefclient_osr_widget_gtk.cpp for linux | |
// browser->GetHost()->SendKeyEvent(...); | |
// browser->GetHost()->SendMouseMoveEvent(...); | |
// browser->GetHost()->SendMouseClickEvent(...); | |
// browser->GetHost()->SendMouseWheelEvent(...); | |
} | |
bool shutdown = false; | |
bool js_executed = false; | |
while (!browserClient->closeAllowed()) | |
{ | |
// send events to browser | |
while (!shutdown && SDL_PollEvent(&e) != 0) | |
{ | |
switch (e.type) | |
{ | |
case SDL_QUIT: | |
shutdown = true; | |
browser->GetHost()->CloseBrowser(false); | |
break; | |
case SDL_KEYDOWN: | |
{ | |
CefKeyEvent event; | |
event.modifiers = getKeyboardModifiers(e.key.keysym.mod); | |
event.windows_key_code = getWindowsKeyCode(e.key.keysym); | |
event.type = KEYEVENT_RAWKEYDOWN; | |
browser->GetHost()->SendKeyEvent(event); | |
event.type = KEYEVENT_CHAR; | |
browser->GetHost()->SendKeyEvent(event); | |
} | |
break; | |
case SDL_KEYUP: | |
{ | |
CefKeyEvent event; | |
event.modifiers = getKeyboardModifiers(e.key.keysym.mod); | |
event.windows_key_code = getWindowsKeyCode(e.key.keysym); | |
event.type = KEYEVENT_KEYUP; | |
browser->GetHost()->SendKeyEvent(event); | |
} | |
break; | |
case SDL_WINDOWEVENT: | |
switch (e.window.event) | |
{ | |
case SDL_WINDOWEVENT_SIZE_CHANGED: | |
renderHandler->resize(e.window.data1, e.window.data2); | |
browser->GetHost()->WasResized(); | |
break; | |
case SDL_WINDOWEVENT_FOCUS_GAINED: | |
browser->GetHost()->SetFocus(true); | |
break; | |
case SDL_WINDOWEVENT_FOCUS_LOST: | |
browser->GetHost()->SetFocus(false); | |
break; | |
case SDL_WINDOWEVENT_HIDDEN: | |
case SDL_WINDOWEVENT_MINIMIZED: | |
//browser->GetHost()->SetWindowVisibility(false); | |
browser->GetHost()->WasHidden(true); | |
break; | |
case SDL_WINDOWEVENT_SHOWN: | |
case SDL_WINDOWEVENT_RESTORED: | |
//browser->GetHost()->SetWindowVisibility(true); | |
browser->GetHost()->WasHidden(false); | |
break; | |
case SDL_WINDOWEVENT_CLOSE: | |
e.type = SDL_QUIT; | |
SDL_PushEvent(&e); | |
break; | |
} | |
break; | |
case SDL_MOUSEMOTION: | |
{ | |
CefMouseEvent event; | |
event.x = e.motion.x; | |
event.y = e.motion.y; | |
browser->GetHost()->SendMouseMoveEvent(event, false); | |
} | |
break; | |
case SDL_MOUSEBUTTONUP: | |
{ | |
CefMouseEvent event; | |
event.x = e.button.x; | |
event.y = e.button.y; | |
browser->GetHost()->SendMouseClickEvent(event, translateMouseButton(e.button), true, 1); | |
} | |
break; | |
case SDL_MOUSEBUTTONDOWN: | |
{ | |
CefMouseEvent event; | |
event.x = e.button.x; | |
event.y = e.button.y; | |
browser->GetHost()->SendMouseClickEvent(event, translateMouseButton(e.button), false, 1); | |
} | |
break; | |
case SDL_MOUSEWHEEL: | |
{ | |
int delta_x = e.wheel.x; | |
int delta_y = e.wheel.y; | |
if (SDL_MOUSEWHEEL_FLIPPED == e.wheel.direction) | |
{ | |
delta_y *= -1; | |
} | |
else | |
{ | |
delta_x *= -1; | |
} | |
CefMouseEvent event; | |
browser->GetHost()->SendMouseWheelEvent(event, delta_x, delta_y); | |
} | |
break; | |
} | |
} | |
if (!js_executed && browserClient->isLoaded()) | |
{ | |
js_executed = true; | |
CefRefPtr<CefFrame> frame = browser->GetMainFrame(); | |
frame->ExecuteJavaScript("alert('ExecuteJavaScript works!');", frame->GetURL(), 0); | |
} | |
// let browser process events | |
//CefDoMessageLoopWork(); | |
CefRunMessageLoop(); | |
// render | |
SDL_RenderClear(renderer); | |
renderHandler->render(); | |
// Update screen | |
SDL_RenderPresent(renderer); | |
//Wait two seconds | |
//SDL_Delay(2000); | |
} | |
browser = nullptr; | |
browserClient = nullptr; | |
renderHandler = nullptr; | |
CefShutdown(); | |
SDL_DestroyRenderer(renderer); | |
} | |
} | |
SDL_DestroyWindow(window); | |
SDL_Quit(); | |
return 0; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _KEYBOARD_UTILS_H_ | |
#define _KEYBOARD_UTILS_H_ | |
#include <SDL.h> | |
int getKeyboardModifiers(uint16_t const mod) | |
{ | |
int result = EVENTFLAG_NONE; | |
if (mod & KMOD_NUM) | |
{ | |
result |= EVENTFLAG_NUM_LOCK_ON; | |
} | |
if (mod & KMOD_CAPS) | |
{ | |
result |= EVENTFLAG_CAPS_LOCK_ON; | |
} | |
if (mod & KMOD_CTRL) | |
{ | |
result |= EVENTFLAG_CONTROL_DOWN; | |
} | |
if (mod & KMOD_SHIFT) | |
{ | |
result |= EVENTFLAG_SHIFT_DOWN; | |
} | |
if (mod & KMOD_ALT) | |
{ | |
result |= EVENTFLAG_ALT_DOWN; | |
} | |
return result; | |
} | |
int getWindowsKeyCode(SDL_Keysym const &key) | |
{ | |
int result = 0; | |
bool shift = !!(key.mod & KMOD_SHIFT); | |
bool caps = !!(key.mod & KMOD_CAPS); | |
bool alt_gr = !!(key.mod & KMOD_RALT); | |
bool uppercase = (caps && !shift) || (shift && !caps); | |
// mapped from azerty windows 8 asus laptop | |
switch (key.sym) | |
{ | |
case SDLK_RETURN: | |
result = 13; | |
break; | |
case SDLK_ESCAPE: | |
result = 27; | |
break; | |
case SDLK_BACKSPACE: | |
result = 8; | |
break; | |
case SDLK_TAB: | |
result = 9; | |
break; | |
case SDLK_SPACE: | |
result = 32; | |
break; | |
case SDLK_EXCLAIM: | |
result = uppercase ? 167 : 33; // § : ! | |
break; | |
case SDLK_QUOTEDBL: | |
result = 34; | |
break; | |
case SDLK_HASH: | |
result = 35; | |
break; | |
case SDLK_DOLLAR: | |
result = 36; | |
break; | |
case SDLK_PERCENT: | |
result = 37; | |
break; | |
case SDLK_AMPERSAND: | |
result = 38; | |
break; | |
case SDLK_QUOTE: | |
result = 39; | |
break; | |
case SDLK_LEFTPAREN: | |
result = 40; | |
break; | |
case SDLK_RIGHTPAREN: | |
result = alt_gr ? 93 : uppercase ? 176 : 41; // ] ? ° : ) | |
break; | |
case SDLK_ASTERISK: | |
result = uppercase ? 181 : 42; // µ : * | |
break; | |
case SDLK_PLUS: | |
result = 43; | |
break; | |
case SDLK_COMMA: | |
result = uppercase ? 63 : 44; // '?' : , | |
break; | |
case SDLK_MINUS: | |
result = 45; | |
break; | |
case SDLK_PERIOD: | |
result = 46; | |
break; | |
case SDLK_SLASH: | |
result = 47; | |
break; | |
case SDLK_0: | |
result = alt_gr ? 64 : uppercase ? 48 : 224; // @ ? 0 : à | |
break; | |
case SDLK_1: | |
result = uppercase ? 49 : 38; // 1 : & (KO) | |
break; | |
case SDLK_2: | |
result = alt_gr ? 126 : uppercase ? 50 : 233; // ~ ? 2 : é | |
break; | |
case SDLK_3: | |
result = alt_gr ? 35 : uppercase ? 51 : 34; // # ? 3 : " | |
break; | |
case SDLK_4: | |
result = alt_gr ? 123 : uppercase ? 52 : 39; // { ? 4 : ' | |
break; | |
case SDLK_5: | |
result = alt_gr ? 91 : uppercase ? 53 : 40; // [ ? 5 : ( (KO) | |
break; | |
case SDLK_6: | |
result = alt_gr ? 124 : uppercase ? 54 : 45; // | ? 6 : - | |
break; | |
case SDLK_7: | |
result = alt_gr ? 96 : uppercase ? 55 : 232; // ` ? 7 : è | |
break; | |
case SDLK_8: | |
result = alt_gr ? 92 : uppercase ? 56 : 95; // \ ? 8 : _ | |
break; | |
case SDLK_9: | |
result = alt_gr ? 94 : uppercase ? 57 : 231; // ^ ? 9 : ç | |
break; | |
case SDLK_COLON: | |
result = uppercase ? 47 : 58; // / : : | |
break; | |
case SDLK_SEMICOLON: | |
result = uppercase ? 46 : 59; // . (KO) : ; | |
break; | |
case SDLK_LESS: | |
result = uppercase ? 62 : 60; // > : < | |
break; | |
case SDLK_EQUALS: | |
result = alt_gr ? 125 : uppercase ? 43 : 61; // } ? + : = | |
break; | |
case SDLK_GREATER: | |
result = 62; | |
break; | |
case SDLK_QUESTION: | |
result = 63; | |
break; | |
case SDLK_AT: | |
result = 64; | |
break; | |
case SDLK_LEFTBRACKET: | |
result = 91; | |
break; | |
case SDLK_BACKSLASH: | |
result = 92; | |
break; | |
case SDLK_RIGHTBRACKET: | |
result = 93; | |
break; | |
case SDLK_CARET: | |
result = uppercase ? 168 : 94; // ^ : ¨ | |
break; | |
case SDLK_UNDERSCORE: | |
result = 95; | |
break; | |
case SDLK_BACKQUOTE: | |
result = 96; | |
break; | |
case SDLK_a: | |
result = uppercase ? 65 : 97; | |
break; | |
case SDLK_b: | |
result = uppercase ? 66 : 98; | |
break; | |
case SDLK_c: | |
result = uppercase ? 67 : 99; | |
break; | |
case SDLK_d: | |
result = uppercase ? 68 : 100; | |
break; | |
case SDLK_e: | |
result = uppercase ? 69 : 101; | |
break; | |
case SDLK_f: | |
result = uppercase ? 70 : 102; | |
break; | |
case SDLK_g: | |
result = uppercase ? 71 : 103; | |
break; | |
case SDLK_h: | |
result = uppercase ? 72 : 104; | |
break; | |
case SDLK_i: | |
result = uppercase ? 73 : 105; | |
break; | |
case SDLK_j: | |
result = uppercase ? 74 : 106; | |
break; | |
case SDLK_k: | |
result = uppercase ? 75 : 107; | |
break; | |
case SDLK_l: | |
result = uppercase ? 76 : 108; | |
break; | |
case SDLK_m: | |
result = uppercase ? 77 : 109; | |
break; | |
case SDLK_n: | |
result = uppercase ? 78 : 110; | |
break; | |
case SDLK_o: | |
result = uppercase ? 79 : 111; | |
break; | |
case SDLK_p: | |
result = uppercase ? 80 : 112; | |
break; | |
case SDLK_q: | |
result = uppercase ? 81 : 113; | |
break; | |
case SDLK_r: | |
result = uppercase ? 82 : 114; | |
break; | |
case SDLK_s: | |
result = uppercase ? 83 : 115; | |
break; | |
case SDLK_t: | |
result = uppercase ? 84 : 116; | |
break; | |
case SDLK_u: | |
result = uppercase ? 85 : 117; | |
break; | |
case SDLK_v: | |
result = uppercase ? 86 : 118; | |
break; | |
case SDLK_w: | |
result = uppercase ? 87 : 119; | |
break; | |
case SDLK_x: | |
result = uppercase ? 88 : 120; | |
break; | |
case SDLK_y: | |
result = uppercase ? 89 : 121; | |
break; | |
case SDLK_z: | |
result = uppercase ? 90 : 122; | |
break; | |
} | |
return result; | |
} | |
#endif // _KEYBOARD_UTILS_H_ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment