A minimal, configurable Chromium Embedded Framework (CEF) starter template for building embedded browser applications. This project provides a clean foundation for integrating CEF into your C++ applications, with support for Qt and other GUI frameworks.
- CEF binary distribution (download from CEF Releases)
- C++17 compatible compiler (GCC 7+, Clang 5+, MSVC 2017+)
- CMake 3.18 or higher
- X11 development libraries (Linux):
sudo apt-get install libx11-devorsudo yum install libX11-devel
-
Clone or copy this starter template:
git clone https://github.com/weknowyourgame/Cef-Starter cd Cef-Starter -
Place CEF binary in a standard location:
# Option 1: Extract CEF to third_party/cef/ mkdir -p third_party/cef cd third_party/cef # Extract cef_binary_XXX_linux64.tar.bz2 here # Option 2: Set CEF_ROOT environment variable export CEF_ROOT=/path/to/cef_binary_XXX_linux64
-
Build the project:
mkdir build && cd build cmake .. make
-
Run the application:
./cef_starter # Or with a custom URL: ./cef_starter --url=https://www.example.com
The CMake build system supports extensive configuration via command-line variables:
PROJECT_NAME- Project name (default:cef_starter)EXECUTABLE_NAME- Output executable name (default: same asPROJECT_NAME)
DEFAULT_URL- Default URL to load when no--urlargument is provided (default:https://www.chromium.org)WINDOW_TITLE- Window title bar text (default: same asPROJECT_NAME)USE_SANDBOX- Enable CEF sandbox for enhanced security (default:OFF)CEF_ENABLE_OFFSCREEN- Enable off-screen rendering support (default:OFF)
CEF_ROOT- Path to CEF binary directory. Auto-detected from:third_party/cef/cef_binary_*../third_party/cef/cef_binary_*../../third_party/cef/cef_binary_*$ENV{CEF_ROOT}
cmake -DPROJECT_NAME=my_browser \
-DEXECUTABLE_NAME=my_app \
-DDEFAULT_URL=https://myapp.local \
-DWINDOW_TITLE="My Application" \
-DUSE_SANDBOX=ON \
..
makecef_starter/
├── main.cpp # Entry point - CEF initialization and message loop
├── app.h/cpp # CefApp implementation - creates browser window
├── client.h/cpp # CefClient implementation - handles browser lifecycle
├── CMakeLists.txt # CMake build configuration
├── BUILD.bazel # Bazel build configuration (optional)
└── build/ # Build output directory (gitignored)
Handles the complete CEF lifecycle:
- Sub-process detection and execution
- CEF initialization with configurable settings
- Message loop management
- Clean shutdown
Key responsibilities:
- Sets up resource paths for CEF
- Configures sandbox settings
- Initializes the App instance
- Runs the blocking message loop until shutdown
Implements CefApp and CefBrowserProcessHandler:
- Creates the browser window when CEF context is ready
- Configures window properties (title, runtime style)
- Handles URL loading (command-line override or default)
The OnContextInitialized() callback is where the first browser window is created.
Implements CefClient and CefLifeSpanHandler:
- Tracks all browser instances
- Handles window close events properly
- Quits the message loop when the last window closes
This ensures clean shutdown when users close all browser windows.
The starter uses CEF_RUNTIME_STYLE_ALLOY which provides:
- Minimal embedded browser UI
- No Chrome tabs, profiles, or browser chrome
- Just the web content in a native window
- Perfect for embedded applications
To change this, modify app.cpp:
window_info.runtime_style = CEF_RUNTIME_STYLE_CHROME; // Full Chrome UI
// or
window_info.runtime_style = CEF_RUNTIME_STYLE_DEFAULT; // Default styleTo add functionality like navigation events, loading callbacks, or display updates:
-
Extend the Client class (
client.h):class Client : public CefClient, public CefLifeSpanHandler, public CefDisplayHandler, // Add navigation/title updates public CefLoadHandler { // Add loading state callbacks public: // CefClient methods CefRefPtr<CefDisplayHandler> GetDisplayHandler() override { return this; } CefRefPtr<CefLoadHandler> GetLoadHandler() override { return this; } // CefDisplayHandler methods void OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) override; // CefLoadHandler methods void OnLoadStart(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, TransitionType transition_type) override; };
-
Implement the handlers in
client.cpp
Modify main.cpp to parse additional arguments:
CefRefPtr<CefCommandLine> command_line = CefCommandLine::GetGlobalCommandLine();
if (command_line->HasSwitch("debug")) {
// Enable debug mode
}
std::string custom_arg = command_line->GetSwitchValue("custom");
if (!custom_arg.empty()) {
// Use custom argument
}Modify app.cpp in OnContextInitialized():
CefBrowserSettings browser_settings;
browser_settings.plugins = STATE_DISABLED; // Disable plugins
browser_settings.javascript = STATE_ENABLED; // Enable JavaScript
browser_settings.web_security = STATE_ENABLED; // Enable web securityThis starter is designed to work with Qt. Here are integration approaches:
Run CEF in a separate thread for best isolation:
class QCefWidget : public QWidget {
Q_OBJECT
public:
QCefWidget(QWidget* parent = nullptr);
~QCefWidget();
void loadUrl(const QString& url);
signals:
void titleChanged(const QString& title);
void urlChanged(const QString& url);
private:
void initializeCEF();
void messageLoopThread();
CefRefPtr<Client> client_;
CefRefPtr<CefBrowser> browser_;
QThread* cef_thread_;
};Use a QTimer to periodically process CEF messages:
class QCefWidget : public QWidget {
Q_OBJECT
private slots:
void processCEFMessages() {
CefDoMessageLoopWork();
}
private:
QTimer* message_timer_;
};
// In constructor:
message_timer_ = new QTimer(this);
connect(message_timer_, &QTimer::timeout, this, &QCefWidget::processCEFMessages);
message_timer_->start(10); // Every 10msFor more control, use off-screen rendering:
- Enable OSR in CMake:
-DCEF_ENABLE_OFFSCREEN=ON - Implement
CefRenderHandlerin your Client - Render to QPixmap/QImage and display in a QLabel
To embed the CEF window in an existing native window:
- Get the window handle from CEF after browser creation
- Reparent the window using platform-specific APIs
- Handle resize events to update browser bounds
If CMake can't find CEF:
cmake -DCEF_ROOT=/absolute/path/to/cef_binary_XXX_linux64 ..- "CEF_INCLUDE_DIR not found": Ensure CEF_ROOT points to the correct directory containing
include/subdirectory - "Unknown CMake command SET_LIBRARY_TARGET_PROPERTIES": This is a CEF CMake issue - ensure CMAKE_BUILD_TYPE is set before project()
- Missing X11 libraries: Install
libx11-dev(Debian/Ubuntu) orlibX11-devel(RedHat/CentOS)
- "Error loading V8 startup snapshot file": Ensure
v8_context_snapshot.binis copied to the build directory (handled automatically by CMake) - "GPU process launch failed": This is often harmless for basic browsing. To disable GPU acceleration, add to browser settings:
browser_settings.chrome_status = STATE_DISABLED;
- Check that X11 is available:
echo $DISPLAY - Ensure window manager is running
- Check console output for error messages
The CMakeLists.txt includes Windows-specific configuration. You'll need:
- Visual Studio 2017 or later
- CEF Windows binary distribution
- Set
CEF_ROOTto the Windows CEF directory
macOS support is configured in CMakeLists.txt. Requirements:
- Xcode with command-line tools
- CEF macOS binary distribution
- Set
CEF_ROOTappropriately
- CEF binary distribution matching your platform
- C++17 compatible compiler
- CMake 3.18+
- Platform-specific dependencies:
- Linux: X11 development libraries
- Windows: Visual Studio runtime
- macOS: Xcode command-line tools
This starter template follows the same license as CEF (BSD-style). See your CEF distribution's LICENSE file for details.
This is a starter template - feel free to fork and customize for your needs. If you have improvements that would benefit others, contributions are welcome.
This starter is compatible with CEF 139+ (Chromium 139+). For older CEF versions, some API calls may need adjustment.