作成者: BlinkGTK Project
対象の版: BlinkGTK 1.2.5-build2 (Chromium
156.0.8078.47)
CEF (Chromium Embedded Framework) や Electron
で作ったアプリを、BlinkGTK に移すか
どうかを判断するためのガイドです。今の構成でやっていることごとに、BlinkGTK
で
何を使うかを表にしました。BlinkGTK
に対応するものが無い機能は、表の中と
「対応していない機能と違うところ」にはっきり書いています。
表に出てくる BlinkGTK の関数は、すべて配布物の公開ヘッダ
blink_gtk/blink_gtk.h
にあるものです。
WebKitGTK から移る方は WebKitGTK
からの移行ガイド
を読んでください。こちらは置き換えがほぼ 1 対 1 です。
乗り換えの前に、次の 4 点が今のアプリの条件に合うかを確かめてください。
| 項目 | BlinkGTK の条件 |
|---|---|
| 対応 OS | Linux の Wayland セッション専用です。X11 では動きません。Windows 向けには BlinkGTK for Windows を別に配布しています。macOS 版はありません |
| ツールキット | GTK4 のアプリとして作ります。窓・メニュー・ダイアログは GTK のものを使います |
| アプリの言語 | C。GObject Introspection を通して Python などからも使えます |
| Node.js | 含みません。Electron のメインプロセスに書いていた処理は、C や Python などで書き直します |
CEF や Electron のように 1 つのコードで Windows・macOS・Linux
に配る構成が要件なら、
BlinkGTK は向きません。
blink_web_view_new() が返すのはGtkWidget* です。GtkWindow や
GtkBox に入れれば表示されます。CEF のCefWindowInfo や Electron の BrowserWindow
のように、エンジンが窓を持つことはありませんCefExecuteProcess()
に当たる関数はありません。サブプロセス用の実行ファイルもCefClient にblink_gtk_run_main_loop() を使い、g_main_loop_run() や g_application_run()
は使いません (後述)。g_timeout_add() と g_idle_add()
はこのループの下でも動きますipcMain /ipcRenderer
に当たる双方向のメッセージがあります。ページの中に C の関数をCefV8Handler など)
はありません表の「BlinkGTK」の列は、公開ヘッダにある関数・シグナル名です。シグナルは
g_signal_connect() で接続します。
| やりたいこと | CEF | Electron | BlinkGTK |
|---|---|---|---|
| サブプロセスの入口 | CefExecuteProcess() |
(不要) | 不要。対応する関数はありません |
| エンジンの初期化 | CefInitialize() |
app.whenReady() を待つ |
blink_gtk_init(&argc, &argv)。GTK や
GLib の呼び出しより前にメインスレッドで呼びます。戻り値が
FALSE なら失敗です |
| リソースの置き場所の指定 | CefSettings の resources_dir_path /
locales_dir_path |
(Electron が自分で持つので指定しない) | blink_gtk_set_resources_path()。ICU
のデータだけ別に置くなら
blink_gtk_set_icu_data_path()。どちらも
blink_gtk_init() より前に呼びます |
| 描画方式の指定 | コマンドラインのスイッチ | app.disableHardwareAcceleration() など |
blink_gtk_set_gpu_mode()。blink_gtk_init()
より前に呼びます |
| 終了 | CefShutdown() |
app.quit() |
blink_gtk_quit_main_loop()
でループを抜けた後、blink_gtk_shutdown() |
| 版の確認 | cef_version_info() |
process.versions |
blink_gtk_get_version() /
blink_gtk_get_chromium_version() |
リソースは、指定しなければ実行ファイルと同じディレクトリを探し、そこに無ければ
ライブラリの置き場所から探します。RPM / DEB
で入れた場合は指定は要りません。
| やりたいこと | CEF | Electron | BlinkGTK |
|---|---|---|---|
| ループを回す | CefRunMessageLoop() |
(Electron が回す) | blink_gtk_run_main_loop() |
| ループを抜ける | CefQuitMessageLoop() |
app.quit() |
blink_gtk_quit_main_loop() |
| アプリのループから少しずつ回す | CefDoMessageLoopWork() |
— | ありません。ループは
blink_gtk_run_main_loop() に任せ、アプリの処理は GLib
のタイマーやシグナルで行います |
GtkApplication で作る |
— | — | blink_gtk_application_run()。g_application_run()
の代わりに使います。最後の窓が閉じるとループを抜けます |
| 多重起動を防ぐ | — | app.requestSingleInstanceLock() |
blink_gtk_application_run() は
GApplication の単一インスタンスの動作に従います。2
つ目の起動は 1 つ目に引き継がれます |
g_application_run()
で回すと、ビルドも起動もできるのにページが読み込まれません。
GtkApplication を使うときは必ず
blink_gtk_application_run() を使ってください。
| やりたいこと | CEF | Electron | BlinkGTK |
|---|---|---|---|
| WebView を作る | CefBrowserHost::CreateBrowser() |
new BrowserWindow() |
blink_web_view_new()。返り値の GtkWidget*
を gtk_window_set_child() などで窓に入れます |
| 窓を作る | CefWindowInfo / Views |
BrowserWindow |
GTK の gtk_window_new() |
| 1 つの窓に複数の WebView を置く | 複数のブラウザ | WebContentsView |
WebView を複数作り、GtkBox などに並べます |
| 保存領域を分ける (cookie・キャッシュ) | CefRequestContext |
session.fromPartition() |
blink_web_view_new_with_profile("名前") |
| 新しい窓を開く要求に答える | CefLifeSpanHandler::OnBeforePopup() |
webContents.setWindowOpenHandler() |
decide-navigation シグナル。新しい窓の場合は
blink_navigation_decision_create_view() で WebView
を受け取り、アプリの窓に入れます |
| 全画面の要求に答える | CefDisplayHandler::OnFullscreenModeChange() |
enter-html-full-screen |
blink_web_view_set_fullscreen_handler() |
BLINK_WEB_VIEW() で BlinkWebView*
に変換してから BlinkGTK の関数に渡します。
GTK の関数にはそのまま渡せます。
| やりたいこと | CEF | Electron | BlinkGTK |
|---|---|---|---|
| URL を開く | CefFrame::LoadURL() |
loadURL() |
blink_web_view_load_uri() |
| HTML の文字列を表示する | data URL | loadURL('data:...') |
blink_web_view_load_html(view, html, base_uri) |
| 戻る・進む・再読み込み・中止 | GoBack() / GoForward() /
Reload() / StopLoad() |
navigationHistory.goBack() など |
blink_web_view_go_back() / go_forward() /
reload() / stop() |
| 戻れるか・進めるか | CanGoBack() / CanGoForward() |
navigationHistory.canGoBack() など |
blink_web_view_can_go_back() /
can_go_forward() |
| 読み込みの状態を受ける | CefLoadHandler |
did-finish-load など |
load-changed シグナル (BLINK_LOAD_STARTED
/ COMMITTED / FINISHED)、失敗は
load-failed |
| タイトル・URL の変化を受ける | CefDisplayHandler::OnTitleChange() /
OnAddressChange() |
page-title-updated / did-navigate |
title-changed / uri-changed シグナル。値は
blink_web_view_get_title() / get_uri()
で取ります |
| ページが始めた移動を止める・外部で開く | CefRequestHandler::OnBeforeBrowse() |
will-navigate |
decide-navigation
シグナルで、blink_navigation_decision_use() /
ignore() / open_external() を呼びます |
load-changed の BLINK_LOAD_STARTED
は、blink_web_view_load_uri() や
load_html()
を呼んだときにだけ届きます。リンクのクリックや戻る・進むでは届きません。
詳しくは シグナル API
を見てください。
| やりたいこと | CEF | Electron | BlinkGTK |
|---|---|---|---|
| ページで JavaScript を実行する | CefFrame::ExecuteJavaScript() |
webContents.executeJavaScript() |
blink_web_view_execute_javascript(view, script, callback, user_data)。結果は
JSON の文字列でコールバックに届きます。Promise ではありません |
| ページより先にスクリプトを入れる | CefRenderProcessHandler::OnContextCreated() |
preload スクリプト | blink_web_view_inject_user_script(view, script, TRUE)。最初の
blink_web_view_load_uri() より前に登録します |
| CSS を入れる | JavaScript で挿入 | webContents.insertCSS() |
blink_web_view_inject_user_stylesheet() |
| ページ → アプリへ送る | CefMessageRouter (window.cefQuery) |
ipcRenderer.send() → ipcMain.on() |
ページで
window.blinkgtk.postMessage('名前', data)、アプリで
blink_web_view_register_message_handler() |
| アプリ → ページへ送る | ExecuteJavaScript() で関数を呼ぶ |
webContents.send() → ipcRenderer.on() |
アプリで
blink_web_view_send_message_to_page()、ページで
window.blinkgtk.addMessageHandler('名前', fn) |
| ページに見せる API を決める | CefV8Handler で関数を登録 |
contextBridge.exposeInMainWorld() |
対応するものはありません。window.blinkgtk
の 2 つの関数でやり取りし、ページの側でラッパー関数を
inject_user_script() で入れます |
| 問い合わせて返事を待つ | cefQuery の成功・失敗のコールバック |
ipcRenderer.invoke() /
ipcMain.handle() |
対応するものはありません。ページから
postMessage で送り、アプリから
send_message_to_page() で返します。要求と返事を対応付ける
ID はページの側で付けてください |
メッセージで気を付けること:
data は Base64
のバイト列です。g_base64_decode()
で戻します。send_message_to_page()
は送り先が無いと黙って捨てます。ページから「準備ができた」とaddMessageHandler()
の登録は、ページを移るたびに消えます。アプリの側の登録は残りますpostMessage
はページの中のどのスクリプトからも呼べます。届いた内容はアプリの側で検証してください詳しくはアプリ連携 API の
「JS
↔︎ C メッセージング」を見てください。
| やりたいこと | CEF | Electron | BlinkGTK |
|---|---|---|---|
| スキーム名を登録する | CefApp::OnRegisterCustomSchemes() |
protocol.registerSchemesAsPrivileged() |
blink_gtk_register_scheme("myapp") を
blink_gtk_init()
より前に呼びます。app・res・resource・ebook・blinkgtk
は最初から登録されています |
| スキームの応答を返す | CefRegisterSchemeHandlerFactory() +
CefResourceHandler |
protocol.handle() |
blink_web_view_register_custom_scheme_full()。コールバックが
URI を受け取り、本文を GBytes で、種類を
out_mime で返します |
| アプリ内の HTML を表示する | 独自スキームか file:// |
loadFile() |
独自スキームで返すのが確実です。独自スキームのページは HTTPS
と同じ扱いになり、fetch() も使えます |
| http / https の要求を横取りする | CefResourceRequestHandler |
session.webRequest /
protocol.handle('https') |
ありません |
スキームの応答で気を付けること:
app://reader/... と app://settings/...
のように URI のホスト名やパスで区別してくださいrealpath()
などで解決し、| やりたいこと | CEF | Electron | BlinkGTK |
|---|---|---|---|
| 開発者ツールを開く | CefBrowserHost::ShowDevTools() |
webContents.openDevTools() |
blink_web_view_open_devtools(view, 0)
でリモートデバッグを開き、blink_web_view_get_devtools_url()
の URL を Chrome / Chromium で開くか、chrome://inspect
から接続します |
| 決まったポートで待ち受ける | CefSettings の remote_debugging_port |
--remote-debugging-port |
blink_web_view_open_devtools(view, ポート番号) |
| 開発者ツールを閉じる | CloseDevTools() |
closeDevTools() |
blink_web_view_close_devtools() |
| 開発者ツールを使わせない | — | devTools: false |
blink_web_view_set_content_policy() に
BLINK_CONTENT_POLICY_NO_DEVTOOLS |
アプリの窓の中に開発者ツールを並べて出す機能はありません。開発者ツールは
別の Chrome / Chromium の窓で開きます。
| やりたいこと | CEF | Electron | BlinkGTK |
|---|---|---|---|
| alert / confirm / prompt | CefJSDialogHandler |
(Electron が出す) | blink_web_view_set_javascript_dialog_handler() |
| ファイルの選択 | CefDialogHandler |
dialog.showOpenDialog() |
ページの <input type="file"> には
blink_web_view_set_file_chooser_handler()。アプリから出すなら
GTK の GtkFileDialog |
| ダウンロード | CefDownloadHandler |
will-download |
blink_web_view_set_download_handler()。進み具合は
download-started シグナルで受け取る
BlinkDownload から |
| HTTP 認証 | CefRequestHandler::GetAuthCredentials() |
login イベント |
blink_web_view_set_auth_handler() |
| 証明書のエラー | CefRequestHandler::OnCertificateError() |
certificate-error |
blink_web_view_set_certificate_error_handler() |
| 権限の要求 (位置情報・カメラ等) | CefPermissionHandler |
session.setPermissionRequestHandler() |
permission-request シグナル。応答は
blink_permission_request_allow() /
deny()。何もしなければ拒否です |
| cookie | CefCookieManager |
session.cookies |
blink_web_view_get_cookies() /
set_cookie() / delete_all_cookies() |
| User-Agent | CefSettings の user_agent |
setUserAgent() |
blink_web_view_set_user_agent() |
| ページ内の検索 | CefBrowserHost::Find() |
findInPage() |
blink_web_view_find_in_page() /
stop_finding() |
| 拡大率 | SetZoomLevel() |
setZoomFactor() |
blink_web_view_set_zoom_level() |
| PDF に書き出す | PrintToPDF() |
printToPDF() |
blink_web_view_print_to_pdf_with_options() |
| 画面を画像にする | — | capturePage() |
blink_web_view_capture_screenshot() /
_async() |
| やりたいこと | CEF | Electron | BlinkGTK |
|---|---|---|---|
| ビルド | CEF のバイナリ配布物と CMake | npm | pkg-config --cflags --libs blinkgtk-0.1
でコンパイル・リンクします |
| 実行に要るもの | CEF の共有ライブラリとリソースをアプリと一緒に配る | Electron の実行ファイルごと配る | runtime のパッケージ (RPM: blinkgtk-bin、DEB:
libblinkgtk-0.1-0)。GTK4 は OS のものを使います |
| ビルドに要るもの | — | — | devel のパッケージ (RPM: blinkgtk-bin-devel、DEB:
libblinkgtk-0.1-dev)。Python などから使うなら gir
のパッケージ (RPM: blinkgtk-bin-gir) も入れます |
| アプリとまとめて配る | アプリのディレクトリに入れて配る | electron-builder など | ポータブルアプリ作成ガイド の手順で tar.gz にまとめます |
リンクは pkg-config を通してください。手で
-lblinkgtk だけを書くと、
ビルドも起動もできるのにページが表示されないことがあります。理由と確かめ方は
組み込みガイド
にあります。
アプリと一緒に配る場合は、THIRD_PARTY_NOTICES.html
などのライセンス表記も
一緒に配る必要があります。ポータブルアプリ作成ガイドの
「ライセンス上の義務」を見てください。
独自スキーム app:// でアプリ内の HTML
を返し、ページのボタンからアプリへ
メッセージを送り、アプリから返事を返す例です。Electron の
ipcRenderer.send() と webContents.send()
の組み合わせに当たります。
/* cef-electron-migration.c */
#include <blink_gtk/blink_gtk.h>
#include <gtk/gtk.h>
#include <string.h>
static const char page_html[] =
"<!doctype html><meta charset='utf-8'><title>sample</title>"
"<button id='b'>アプリに送る</button><p id='out'></p>"
"<script>"
"window.blinkgtk.addMessageHandler('reply', function (data) {"
" document.getElementById('out').textContent = data;"
"});"
"document.getElementById('b').onclick = function () {"
" window.blinkgtk.postMessage('hello', 'ping');"
"};"
"</script>";
/* app://main/... への要求ごとに呼ばれる。URI 以外の情報は渡らない */
static GBytes* on_app_scheme(BlinkWebView* view, const char* uri,
char** out_mime, gpointer user_data) {
(void)view; (void)uri; (void)user_data;
*out_mime = g_strdup("text/html");
return g_bytes_new_static(page_html, strlen(page_html));
}
/* ページの postMessage('hello', ...) を受ける。data は Base64 */
static void on_hello(const char* name, const guint8* data, gsize length,
gpointer user_data) {
BlinkWebView* view = BLINK_WEB_VIEW(user_data);
gchar* encoded = g_strndup((const gchar*)data, length);
gsize decoded_len = 0;
guchar* decoded = g_base64_decode(encoded, &decoded_len);
g_print("%s: %.*s\n", name, (int)decoded_len, (const char*)decoded);
blink_web_view_send_message_to_page(view, "reply", "pong");
g_free(decoded);
g_free(encoded);
}
/* execute_javascript の結果。JSON の文字列で、実行できなければ空文字列 */
static void on_title(const char* result, gpointer user_data) {
(void)user_data;
g_print("document.title = %s\n", result[0] ? result : "(未実行)");
}
static void on_load_changed(BlinkWebView* view, BlinkLoadEvent event,
gpointer user_data) {
(void)user_data;
if (event == BLINK_LOAD_FINISHED)
blink_web_view_execute_javascript(view, "document.title", on_title, NULL);
}
static gboolean on_close(GtkWindow* window, gpointer user_data) {
(void)window; (void)user_data;
blink_gtk_quit_main_loop();
return FALSE;
}
int main(int argc, char* argv[]) {
/* CefInitialize() に当たる。GTK より先に呼ぶ */
if (!blink_gtk_init(&argc, &argv)) {
g_printerr("blink_gtk_init() failed\n");
return 1;
}
GtkWidget* window = gtk_window_new();
gtk_window_set_title(GTK_WINDOW(window), "BlinkGTK");
gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
g_signal_connect(window, "close-request", G_CALLBACK(on_close), NULL);
/* CreateBrowser() / new BrowserWindow() に当たる */
GtkWidget* web_view = blink_web_view_new();
gtk_window_set_child(GTK_WINDOW(window), web_view);
BlinkWebView* view = BLINK_WEB_VIEW(web_view);
blink_web_view_register_custom_scheme_full(view, "app", on_app_scheme, NULL);
blink_web_view_register_message_handler(view, "hello", on_hello, view);
g_signal_connect(web_view, "load-changed", G_CALLBACK(on_load_changed), NULL);
blink_web_view_load_uri(view, "app://main/index.html");
gtk_window_present(GTK_WINDOW(window));
/* CefRunMessageLoop() に当たる */
int status = blink_gtk_run_main_loop();
/* CefShutdown() に当たる */
blink_gtk_shutdown();
return status;
}ビルドと実行:
cc cef-electron-migration.c -o cef-electron-migration $(pkg-config --cflags --libs blinkgtk-0.1)
./cef-electron-migrationWayland のセッションで実行してください。ボタンを押すと、端末に
hello: ping と出て、
ページに pong と表示されます。
次の機能は、BlinkGTK の公開 API に対応するものがありません。
| 機能 | CEF / Electron での呼び方 | 代わりの方法 |
|---|---|---|
| macOS 版・X11 での動作 | (両方とも対応) | ありません |
| Node.js | Electron のメインプロセスと preload | C や GObject Introspection 経由の言語で書きます |
| アプリのループから少しずつ回す | CefDoMessageLoopWork() |
blink_gtk_run_main_loop() に任せ、GLib
のタイマーを使います |
| 画面外で描いて画素を受け取る | CEF の windowless rendering (OnPaint) |
ありません。1 枚ずつなら
blink_web_view_capture_screenshot() |
| ページに C の関数を直接見せる | CefV8Handler / contextBridge |
window.blinkgtk
のメッセージと、inject_user_script()
で入れるラッパー関数 |
| 問い合わせと返事の組 | cefQuery / ipcRenderer.invoke() |
メッセージを 2 本組み合わせます |
| http / https の要求の横取り・書き換え | CefResourceRequestHandler /
session.webRequest |
ありません。アプリが返す内容は独自スキームで配ります |
| スキームの応答の状態コード・ヘッダ・リダイレクト | CefResourceHandler / protocol.handle() の
Response |
ありません。本文と種類だけを返せます |
| 印刷ダイアログ | CefBrowserHost::Print() /
webContents.print() |
ありません。PDF への書き出しは
blink_web_view_print_to_pdf_with_options() |
| アプリの窓の中の開発者ツール | ShowDevTools() / openDevTools() |
リモートデバッグを別の Chrome / Chromium で開きます |
| 拡張機能 | CEF・Electron の拡張機能の読み込み | ありません |
| アプリの自動更新 | Electron の autoUpdater |
ありません。OS のパッケージ管理で更新します |
| メニュー・トレイ・通知 | Electron の Menu / Tray /
Notification |
GTK と GLib の機能 (GMenu、GNotification
など) を使います |
ほかに、次の点が違います。
blink_gtk_init() の後では変えられませんblink_web_view_get_uri() と
get_title() の返り値は解放しません。
次に同じ関数を足りない機能が乗り換えの妨げになる場合は、
不具合報告・要望
または
contact@blinkgtk.org
までお知らせください。
| 日付 | 内容 |
|---|---|
| 2026-10-19 | 初版 |