Skip to content
Snippets Groups Projects
Commit b9d08079 authored by ezsh's avatar ezsh
Browse files

Make FCHost compile on Linux/X11

Linux handlers and main() were copied from the cefsimple test app,
persistent storage implementation rewritten using cstdio and
std::filesystem to make it cross-platform.
parent 2d99feab
No related branches found
No related tags found
1 merge request!9085Port FCHost to Linux/X11
......@@ -97,6 +97,9 @@ set(CMAKE_CONFIGURATION_TYPES Debug Release)
# Project name.
project(fchost)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Use folders in the resulting project files.
set_property(GLOBAL PROPERTY OS_FOLDERS ON)
......@@ -113,7 +116,7 @@ set_property(GLOBAL PROPERTY OS_FOLDERS ON)
# Set the CEF_ROOT environment variable before executing CMake. For example:
# > set CEF_ROOT=c:\path\to\cef_binary_3.2704.xxxx.gyyyyyyy_windows32
#
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
#
......
......@@ -4,8 +4,6 @@
#include "fchost_app.h"
#include <string>
#include "include/cef_browser.h"
#include "include/cef_command_line.h"
#include "include/views/cef_browser_view.h"
......@@ -14,6 +12,13 @@
#include "fchost_handler.h"
#include "fchost_storage_js.h"
#include <filesystem>
#include <string>
#if defined(OS_LINUX)
#include <unistd.h>
#endif
namespace {
// When using the Views framework this object provides the delegate
......@@ -55,6 +60,36 @@ class SimpleWindowDelegate : public CefWindowDelegate {
DISALLOW_COPY_AND_ASSIGN(SimpleWindowDelegate);
};
std::filesystem::path executablePath() {
#if defined(OS_WIN)
wchar_t target_path[_MAX_PATH];
GetModuleFileNameW(NULL, target_path, _MAX_PATH);
return std::filesystem::path(target_path);
#elif defined(OS_LINUX)
std::string buf;
buf.resize(32); // initial size estimate
for (;; ) {
ssize_t ret = readlink("/proc/self/exe", buf.data(), buf.size());
if (ret == -1) {
perror("getexename() failed");
break;
}
if (static_cast<std::size_t>(ret) >= buf.size()) { // >= because we need the terminating NUL too
buf.resize(buf.size() * 2);
continue;
}
return buf;
}
return {};
#else
#error "Platform-specific code required"
#endif
}
} // namespace
FCHostApp::FCHostApp() {}
......@@ -86,15 +121,11 @@ void FCHostApp::OnContextInitialized() {
// For now, read from external file. Probably want to make this a resource
// at least on Windows.
std::filesystem::path gameHTML = executablePath().parent_path() / "FC_pregmod.html";
#if defined(OS_WIN)
wchar_t target_path[_MAX_PATH];
GetModuleFileNameW(NULL, target_path, _MAX_PATH);
std::wstring url = target_path;
size_t pos = url.find_last_of(L"\\/");
url = url.substr(0, pos);
url += L"/FC_pregmod.html";
std::wstring url = gameHTML.native();
#else
#error "Platform-specific code needed"
std::string url = "file://" + gameHTML.string();
#endif
// Allow some access flexibility for our local file
......@@ -127,4 +158,3 @@ void FCHostApp::OnContextInitialized() {
void FCHostApp::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) {
FCHostStorageRegister(GetLocalStorePath().ToWString() + L"/FCHostPersistentStorage", context->GetGlobal());
}
......@@ -206,7 +206,13 @@ bool FCHostHandler::OnPreKeyEvent(CefRefPtr<CefBrowser> browser,
CefWindowInfo windowInfo;
CefBrowserSettings settings;
CefPoint point;
#if defined (OS_WIN)
windowInfo.SetAsPopup(browser->GetHost()->GetWindowHandle(), "DevTools");
#elif defined(OS_LINUX)
windowInfo.SetAsChild(browser->GetHost()->GetWindowHandle(), CefRect(100, 100, 800, 600));
#else
#error "Platform-specific code required"
#endif
browser->GetHost()->ShowDevTools(windowInfo, browser->GetHost()->GetClient(), settings, point);
return true;
......
......@@ -13,7 +13,8 @@ class FCHostHandler : public CefClient,
public CefLifeSpanHandler,
public CefLoadHandler,
public CefDownloadHandler,
public CefKeyboardHandler {
public CefKeyboardHandler,
private CefDialogHandler {
public:
explicit FCHostHandler(bool use_views);
~FCHostHandler();
......@@ -32,6 +33,10 @@ class FCHostHandler : public CefClient,
virtual CefRefPtr<CefDownloadHandler> GetDownloadHandler() OVERRIDE { return this; }
virtual CefRefPtr<CefKeyboardHandler> GetKeyboardHandler() OVERRIDE { return this; }
CefRefPtr< CefDialogHandler > GetDialogHandler() override { return this; }
bool OnFileDialog(CefRefPtr<CefBrowser> browser, CefDialogHandler::FileDialogMode mode,
const CefString& title, const CefString& default_file_path, const std::vector<CefString>& accept_filters,
int selected_accept_filter, CefRefPtr<CefFileDialogCallback> callback) override;
// CefDisplayHandler methods:
virtual void OnTitleChange(CefRefPtr<CefBrowser> browser,
......
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "fchost_handler.h"
#if defined(CEF_X11)
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#endif
#include "include/base/cef_logging.h"
#include "include/cef_browser.h"
#include <cstdlib>
#include <cstdio>
#include <filesystem>
#include <iomanip>
#include <memory>
#include <sstream>
#include <string>
// #define TRACE
#ifdef TRACE
#include <iostream>
#endif
namespace {
std::string executeProcessAndReadStdOut(const std::string& command)
{
#ifdef TRACE
std::cerr << "Command: " << command << std::endl;
#endif
std::unique_ptr<FILE, decltype(&::pclose)> f(::popen(command.c_str(), "r"), &::pclose);
std::ostringstream output;
const int BUF_SIZE = 1024;
char buf[BUF_SIZE];
while(!std::feof(f.get())){
std::size_t read = std::fread(buf, 1, BUF_SIZE, f.get());
output.write(buf, static_cast<std::streamsize>(read));
}
std::string res = output.str();
return res.substr(0, res.size() - 1); // to clear out CR
}
std::string runKDialog(CefDialogHandler::FileDialogMode mode, const CefString& title, const CefString& default_file_path,
const std::vector<CefString>& accept_filters)
{
std::ostringstream cmdLine;
cmdLine << "kdialog";
if (!title.empty()) {
cmdLine << " --title " << std::quoted(title.ToString());
}
#ifdef TRACE
std::cerr << "mode: " << std::hex << mode << std::dec << std::endl
<< "default_file_path: " << default_file_path << std::endl
<< "accept_filters: " << std::endl;
for (const auto& f: accept_filters) {
std::cerr << f.ToString() << std::endl;
}
#endif
// kdialog does not support these
mode = static_cast<CefDialogHandler::FileDialogMode>(mode & ~(CefDialogHandler::FileDialogMode::FILE_DIALOG_OVERWRITEPROMPT_FLAG));
mode = static_cast<CefDialogHandler::FileDialogMode>(mode & ~(CefDialogHandler::FileDialogMode::FILE_DIALOG_HIDEREADONLY_FLAG));
switch (mode) {
case CefDialogHandler::FileDialogMode::FILE_DIALOG_OPEN_MULTIPLE:
cmdLine << " --multiple"; [[fallthrough]]
case CefDialogHandler::FileDialogMode::FILE_DIALOG_OPEN:
cmdLine << " --getopenfilename";
break;
case CefDialogHandler::FileDialogMode::FILE_DIALOG_SAVE:
cmdLine << " --getsavefilename";
break;
case CefDialogHandler::FileDialogMode::FILE_DIALOG_OPEN_FOLDER:
cmdLine << " --getexistingdirectory";
break;
default:
break; // TODO
}
const std::string dsp = default_file_path.ToString();
if (dsp.find('/') != std::string::npos) {
cmdLine << ' ' << std::quoted(dsp);
} else {
// TODO save last used directory and put it here instead of $HOME
cmdLine << ' ' << std::quoted(std::string(getenv("HOME")) + '/' + dsp);
}
if (accept_filters.size()) {
cmdLine << " \'";
for (const auto& f: accept_filters) {
const auto s = f.ToString();
cmdLine << " |" << s << " file(*" << s << ')';
}
cmdLine << '\'';
}
return executeProcessAndReadStdOut(cmdLine.str());
}
std::string runZenity(CefDialogHandler::FileDialogMode mode, const CefString& title, const CefString& default_file_path,
const std::vector<CefString>& accept_filters)
{
std::ostringstream cmdLine;
cmdLine << "zenity --file-selection";
// zenity does not support these
mode = static_cast<CefDialogHandler::FileDialogMode>(mode & ~(CefDialogHandler::FileDialogMode::FILE_DIALOG_HIDEREADONLY_FLAG));
if (mode & CefDialogHandler::FileDialogMode::FILE_DIALOG_OVERWRITEPROMPT_FLAG) {
cmdLine << " --confirm-overwrite";
}
mode = static_cast<CefDialogHandler::FileDialogMode>(mode & ~(CefDialogHandler::FileDialogMode::FILE_DIALOG_OVERWRITEPROMPT_FLAG));
switch (mode) {
case CefDialogHandler::FileDialogMode::FILE_DIALOG_OPEN_MULTIPLE:
cmdLine << " --multiple";
break;
case CefDialogHandler::FileDialogMode::FILE_DIALOG_OPEN:
// this is the implicit default mode for zenity
break;
case CefDialogHandler::FileDialogMode::FILE_DIALOG_SAVE:
cmdLine << " --save";
break;
case CefDialogHandler::FileDialogMode::FILE_DIALOG_OPEN_FOLDER:
cmdLine << " --directory";
break;
default:
break; // TODO ?
}
const std::string dsp = default_file_path.ToString();
if (dsp.find('/') != std::string::npos) {
cmdLine << " --filename=" << std::quoted(dsp);
} else {
// TODO save last used directory and put it here instead of $HOME
cmdLine << " --filename=" << std::quoted(std::string(getenv("HOME")) + '/' + dsp);
}
for (const auto& f: accept_filters) {
const auto s = f.ToString();
cmdLine << " --file-filter=\"" << s << " file | *" << s << '"';
}
return executeProcessAndReadStdOut(cmdLine.str());
}
using DialogRunFunc = std::string (*)(CefDialogHandler::FileDialogMode mode, const CefString& title, const CefString& default_file_path,
const std::vector<CefString>& accept_filters);
class DialogHelper {
public:
DialogHelper();
std::string operator()(CefDialogHandler::FileDialogMode mode, const CefString& title, const CefString& default_file_path,
const std::vector<CefString>& accept_filters) const
{
return func_ ? func_(mode, title, default_file_path, accept_filters) : "";
}
private:
DialogRunFunc func_;
};
DialogHelper::DialogHelper()
{
// we will try to launch kdialog or zenity
std::string dialogExecutable;
// try to determine which environment we run inside
std::string desktop = getenv("XDG_CURRENT_DESKTOP");
const auto checkExeExists = [](const char* name) {
int ec = ::system((std::string(name) + " --help > /dev/null").c_str());
return ec >= 0 && ec < 127;
};
if (desktop == "KDE") {
dialogExecutable = "kdialog";
} else if (desktop == "GNOME") {
dialogExecutable = "zenity";
} else {
// well, let's check executables
if (checkExeExists("zenity")) {
dialogExecutable = "zenity";
} else if (checkExeExists("kdialog")){
dialogExecutable = "kdialog";
}
}
#ifdef TRACE
std::cerr << "dialogExecutable: " << dialogExecutable << std::endl
<< "checkExeExists: " << checkExeExists(dialogExecutable.c_str()) << std::endl;
#endif
if (!dialogExecutable.empty() && checkExeExists(dialogExecutable.c_str())) {
if (dialogExecutable == "kdialog") {
func_ = &runKDialog;
} else if (dialogExecutable == "zenity") {
func_ = &runZenity;
} else {
func_ = nullptr;
}
}
}
}
void FCHostHandler::PlatformTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) {
std::string titleStr(title);
#if defined(CEF_X11)
// Retrieve the X11 display shared with Chromium.
::Display* display = cef_get_xdisplay();
DCHECK(display);
// Retrieve the X11 window handle for the browser.
::Window window = browser->GetHost()->GetWindowHandle();
if (window == kNullWindowHandle)
return;
// Retrieve the atoms required by the below XChangeProperty call.
const char* kAtoms[] = {"_NET_WM_NAME", "UTF8_STRING"};
Atom atoms[2];
int result =
XInternAtoms(display, const_cast<char**>(kAtoms), 2, false, atoms);
if (!result)
NOTREACHED();
// Set the window title.
XChangeProperty(display, window, atoms[0], atoms[1], 8, PropModeReplace,
reinterpret_cast<const unsigned char*>(titleStr.c_str()),
titleStr.size());
// TODO(erg): This is technically wrong. So XStoreName and friends expect
// this in Host Portable Character Encoding instead of UTF-8, which I believe
// is Compound Text. This shouldn't matter 90% of the time since this is the
// fallback to the UTF8 property above.
XStoreName(display, browser->GetHost()->GetWindowHandle(), titleStr.c_str());
#endif // defined(CEF_X11)
}
bool FCHostHandler::OnFileDialog(CefRefPtr<CefBrowser> /* browser */, CefDialogHandler::FileDialogMode mode,
const CefString& title, const CefString& default_file_path, const std::vector<CefString>& accept_filters,
int /* selected_accept_filter */, CefRefPtr<CefFileDialogCallback> callback)
{
static DialogHelper helper;
std::string fn = helper(mode, title, default_file_path, accept_filters);
#ifdef TRACE
std::cerr << "fn: " << fn << std::endl;
#endif
if (fn.empty()) {
callback->Cancel();
return false;
}
std::vector<CefString> selected;
selected.push_back(fn);
callback->Continue(0, selected);
return true;
}
......@@ -26,3 +26,10 @@ void FCHostHandler::PlatformTitleChange(CefRefPtr<CefBrowser> browser,
SetWindowText(hwnd, std::wstring(title).c_str());
}
bool FCHostHandler::OnFileDialog(CefRefPtr<CefBrowser> /* browser */, CefDialogHandler::FileDialogMode /* mode */,
const CefString& /* title */, const CefString& /* default_file_path */, const std::vector<CefString>& /* accept_filters */,
int /* selected_accept_filter */, CefRefPtr<CefFileDialogCallback> /* callback */)
{
return false; // to display the default dialog
}
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "fchost_app.h"
#if defined(CEF_X11)
#include <X11/Xlib.h>
#endif
#include "include/base/cef_logging.h"
#include "include/cef_command_line.h"
#include <filesystem>
CefString FCHostApp::GetLocalStorePath()
{
std::filesystem::path ret = std::filesystem::path{getenv("HOME")} / ".local" / "share" / "FreeCities_Pregmod";
return CefString(ret.string());
}
#if defined(CEF_X11)
namespace {
int XErrorHandlerImpl(Display* display, XErrorEvent* event) {
LOG(WARNING) << "X error received: "
<< "type " << event->type << ", "
<< "serial " << event->serial << ", "
<< "error_code " << static_cast<int>(event->error_code) << ", "
<< "request_code " << static_cast<int>(event->request_code)
<< ", "
<< "minor_code " << static_cast<int>(event->minor_code);
return 0;
}
int XIOErrorHandlerImpl(Display* display) {
return 0;
}
} // namespace
#endif // defined(CEF_X11)
// Entry point function for all processes.
int main(int argc, char* argv[]) {
// Provide CEF with command-line arguments.
CefMainArgs main_args(argc, argv);
// CEF applications have multiple sub-processes (render, plugin, GPU, etc)
// that share the same executable. This function checks the command-line and,
// if this is a sub-process, executes the appropriate logic.
int exit_code = CefExecuteProcess(main_args, nullptr, nullptr);
if (exit_code >= 0) {
// The sub-process has completed so return here.
return exit_code;
}
#if defined(CEF_X11)
// Install xlib error handlers so that the application won't be terminated
// on non-fatal errors.
XSetErrorHandler(XErrorHandlerImpl);
XSetIOErrorHandler(XIOErrorHandlerImpl);
#endif
// Parse command-line arguments for use in this method.
CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
command_line->InitFromArgv(argc, argv);
// It will create the first browser instance in OnContextInitialized() after
// CEF has initialized.
CefRefPtr<FCHostApp> app(new FCHostApp);
// Specify CEF global settings here.
CefSettings settings;
if (command_line->HasSwitch("enable-chrome-runtime")) {
// Enable experimental Chrome runtime. See issue #2969 for details.
settings.chrome_runtime = true;
}
// Cache location is required for local storage
CefString local_storage = app->GetLocalStorePath();
cef_string_from_utf16(local_storage.c_str(), local_storage.length(), &settings.cache_path);
// When generating projects with CMake the CEF_USE_SANDBOX value will be defined
// automatically. Pass -DUSE_SANDBOX=OFF to the CMake command-line to disable
// use of the sandbox.
#if !defined(CEF_USE_SANDBOX)
settings.no_sandbox = true;
#endif
// Initialize CEF for the browser process.
CefInitialize(main_args, settings, app.get(), nullptr);
// Run the CEF message loop. This will block until CefQuitMessageLoop() is
// called.
CefRunMessageLoop();
// Shut down CEF.
CefShutdown();
return 0;
}
#include "fchost_storage.h"
#include <algorithm>
#if defined(OS_WIN)
#include "shlwapi.h"
#endif
#include <cstdio>
#include <memory>
#include <vector>
namespace fs = std::filesystem;
namespace {
struct closeFile {
void operator()(std::FILE* f) const noexcept
{
std::fclose(f);
}
};
}
CefRefPtr<CefV8Value> FCHostSessionStorage::keys() const
{
......@@ -17,7 +28,6 @@ CefRefPtr<CefV8Value> FCHostSessionStorage::keys() const
return ret;
}
#if defined(OS_WIN)
/* This shouldn't happen, so don't waste time on it. Sugarcube really only writes simple alphanumeric keys.
static bool SanitizePath(std::wstring& inpath)
{
......@@ -31,96 +41,89 @@ static bool SanitizePath(std::wstring& inpath)
}
*/
FCHostPersistentStorage::FCHostPersistentStorage(const CefString& _path)
#if defined(OS_WIN)
: path(_path.ToWString())
#else
: path(_path.ToString())
#endif
{
ensure_folder_exists();
load();
}
void FCHostPersistentStorage::set(const CefString& key, CefRefPtr<CefV8Value> val)
{
__super::set(key, val);
base::set(key, val);
// only strings get persisted (should be OK, Sugarcube will serialize first)
if (val->IsString())
{
// we should probably be doing this async but TBT Sugarcube is the slow part, not the file IO
DWORD written;
HANDLE fh = CreateFile(get_filename(key).c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
std::unique_ptr<std::FILE, closeFile> fh;
#if defined(OS_WIN)
fh.reset(_wfopen(get_filename(key).c_str(), L"w"));
#else
fh.reset(std::fopen(get_filename(key).c_str(), "w"));
#endif
CefString valStr = val->GetStringValue();
if (valStr.size() > 0)
{
WriteFile(fh, valStr.c_str(), static_cast<DWORD>(valStr.size() * sizeof(valStr.c_str()[0])), &written, NULL);
if (valStr.size() > 0) {
std::fwrite(valStr.c_str(), static_cast<size_t>(valStr.size() * sizeof(valStr.c_str()[0])), 1, fh.get());
}
CloseHandle(fh);
}
}
bool FCHostPersistentStorage::remove(const CefString& key)
{
bool retval = __super::remove(key);
DeleteFile(get_filename(key).c_str());
return retval;
fs::remove(get_filename(key));
return base::remove(key);
}
void FCHostPersistentStorage::clear()
{
__super::clear();
WIN32_FIND_DATAW w32fd;
HANDLE hFind = FindFirstFile((path + L"\\*").c_str(), &w32fd);
if (hFind != INVALID_HANDLE_VALUE)
{
do
{
if (w32fd.dwFileAttributes & ~(FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE))
{
DeleteFile(get_filename(w32fd.cFileName).c_str());
}
} while (FindNextFile(hFind, &w32fd));
FindClose(hFind);
for (const auto& entry: fs::directory_iterator(path)) {
if (fs::is_regular_file(entry.path())) {
fs::remove_all(entry.path());
}
}
base::clear();
}
void FCHostPersistentStorage::load()
{
constexpr size_t bufsize = 1024 * 1024 * 1024; // 1gb should be enough
char* readbuf = new char[bufsize];
WIN32_FIND_DATAW w32fd;
HANDLE hFind = FindFirstFile((path + L"\\*").c_str(), &w32fd);
if (hFind != INVALID_HANDLE_VALUE)
{
do
{
if (w32fd.dwFileAttributes & ~(FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE))
{
DWORD bytesread = 0;
HANDLE fh = CreateFile(get_filename(w32fd.cFileName).c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (fh)
{
BOOL success = ReadFile(fh, readbuf, static_cast<DWORD>(bufsize - 1), &bytesread, NULL);
if (success)
{
readbuf[bytesread] = L'\0';
readbuf[bytesread+1] = L'\0'; // null terminate
CefString val = reinterpret_cast<wchar_t*>(readbuf);
storage.emplace(w32fd.cFileName, CefV8Value::CreateString(val));
}
CloseHandle(fh);
}
std::vector<char> readbuf;
std::unique_ptr<std::FILE, closeFile> fh;
for (const auto& entry: fs::directory_iterator(path)) {
if (fs::is_regular_file(entry.path())) {
const auto entrySize = fs::file_size(entry.path());
readbuf.resize(static_cast<std::size_t>(entrySize + 2)); // +1 wchar_t
#if defined(OS_WIN)
fh.reset(_wfopen(entry.path().c_str(), L"r"));
#else
fh.reset(std::fopen(entry.path().c_str(), "r"));
#endif
if (std::fread(&readbuf[0], entrySize, 1, fh.get())) {
readbuf[entrySize + 1] = readbuf[entrySize] = 0; // null terminate
CefString val = static_cast<const wchar_t*>(static_cast<const void*>(readbuf.data()));
storage.emplace(entry.path().filename().native(), CefV8Value::CreateString(val));
}
} while (FindNextFile(hFind, &w32fd));
FindClose(hFind);
}
}
delete[] readbuf;
}
std::wstring FCHostPersistentStorage::get_filename(const CefString& key) const
fs::path FCHostPersistentStorage::get_filename(const CefString& key) const
{
return path + L"\\" + key.c_str();
#if defined (OS_WIN)
return path / key.ToWString();
#else
return path / key.ToString();
#endif
}
void FCHostPersistentStorage::ensure_folder_exists() const
{
// ignore returned errors
CreateDirectory(path.c_str(), NULL);
fs::create_directories(path);
}
#else
#error "Platform-specific persistent storage implementation required."
#endif
#pragma once
#include "include/cef_app.h" // probably excessive
#include <filesystem>
#include <map>
#include <string>
......@@ -30,8 +32,9 @@ protected:
// memory-backed, disk-persistent local storage
class FCHostPersistentStorage : public FCHostSessionStorage
{
using base = FCHostSessionStorage;
public:
FCHostPersistentStorage(const std::wstring& _path) : path(_path) { ensure_folder_exists(); load(); };
FCHostPersistentStorage(const CefString& _path);
virtual void set(const CefString& key, CefRefPtr<CefV8Value> val) override;
virtual bool remove(const CefString& key) override;
......@@ -40,7 +43,7 @@ public:
private:
void load();
void ensure_folder_exists() const;
std::wstring get_filename(const CefString& key) const;
std::filesystem::path get_filename(const CefString& key) const;
std::wstring path;
std::filesystem::path path;
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment