X-Git-Url: https://git.sesse.net/?p=nageru;a=blobdiff_plain;f=nageru_cef_app.h;fp=nageru_cef_app.h;h=dff98441db7b2f4e4cc0447a6560694b2d906f54;hp=0000000000000000000000000000000000000000;hb=b68d8a25951faf5b967b7a35fa0a363b4b68fbc0;hpb=25ea462c2847313afd702cea1db29199b845fbd4 diff --git a/nageru_cef_app.h b/nageru_cef_app.h new file mode 100644 index 0000000..dff9844 --- /dev/null +++ b/nageru_cef_app.h @@ -0,0 +1,97 @@ +#ifndef _NAGERU_CEF_APP_H +#define _NAGERU_CEF_APP_H 1 + +// NageruCefApp deals with global state around CEF, in particular the global +// CEF event loop. CEF is pretty picky about which threads everything runs on; +// in particular, the documentation says CefExecute, CefInitialize and +// CefRunMessageLoop must all be on the main thread (ie., the first thread +// created). However, Qt wants to run _its_ event loop on this thread, too, +// and integrating the two has proved problematic (see also the comment in +// main.cpp). It seems that as long as you don't have two GLib loops running, +// it's completely fine in practice to have a separate thread for the main loop +// (running CefInitialize, CefRunMessageLoop, and finally CefDestroy). +// Many other tasks (like most things related to interacting with browsers) +// have to be run from the message loop, but that's fine; CEF gives us tools +// to post tasks to it. + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// Takes in arbitrary lambdas and converts them to something CefPostTask() will accept. +class CEFTaskAdapter : public CefTask +{ +public: + CEFTaskAdapter(const std::function& func) + : func(func) {} + void Execute() override { func(); } + +private: + std::function func; + + IMPLEMENT_REFCOUNTING(CEFTaskAdapter); +}; + +// Runs and stops the CEF event loop, and also makes some startup tasks. +class NageruCefApp : public CefApp, public CefRenderProcessHandler, public CefBrowserProcessHandler { +public: + NageruCefApp() {} + + // Starts up the CEF main loop if it does not already run, and blocks until + // CEF is properly initialized. You can call initialize_ref() multiple times, + // which will then increase the refcount. + void initialize_cef(); + + // If the refcount goes to zero, shut down the main loop and uninitialize CEF. + void unref_cef(); + + // Closes the given browser, and blocks until it is done closing. + // + // NOTE: We can't call unref_cef() from close_browser(), since + // CefRefPtr does not support move semantics, so it would have a + // refcount of either zero or two going into close_browser (not one, + // as it should). The latter means the caller would hold on to an extra + // reference to the browser (which triggers an assert failure), and the + // former would mean that the browser gets deleted before it's closed. + void close_browser(CefRefPtr browser); + + CefRefPtr GetRenderProcessHandler() override + { + return this; + } + + CefRefPtr GetBrowserProcessHandler() override + { + return this; + } + + void OnBeforeCommandLineProcessing(const CefString& process_type, CefRefPtr command_line); + + void OnBrowserDestroyed(CefRefPtr browser) override; + +private: + void cef_thread_func(); + + std::thread cef_thread; + std::mutex cef_mutex; + int cef_thread_refcount = 0; // Under . + bool cef_initialized = false; // Under . + std::condition_variable cef_initialized_cond; + std::unordered_set pending_browsers; // Under . + std::condition_variable browser_closed_cond; + + IMPLEMENT_REFCOUNTING(NageruCefApp); +}; + +#endif // !defined(_NAGERU_CEF_APP_H)