diff --git a/.gitignore b/.gitignore index 3d43c45..c6cc349 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,16 @@ -# The directory I like to build under + +# Common build directories /build +/build_x64 +/build_x86 + +# Distribution directories +/dist # Dependencies that are downloaded/installed separately -/thirdparty/cef3/* -!/thirdparty/cef3/README.txt \ No newline at end of file +/thirdparty/cef3/** +!/thirdparty/cef3/README.txt + +# IDE files +/.vscode +/.idea diff --git a/CMakeLists.txt b/CMakeLists.txt index 415769f..9e751f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,99 +1,78 @@ -if(WIN32) - # Required for MSVC_RUNTIME_LIBRARY - cmake_minimum_required(VERSION 3.15) - cmake_policy(SET CMP0091 NEW) -else() - cmake_minimum_required(VERSION 2.8.7) -endif() - -project(gmod-html LANGUAGES CXX) -# We can only do release builds -set(CMAKE_CONFIGURATION_TYPES Release Debug) -set(CMAKE_CXX_STANDARD 11) +cmake_minimum_required(VERSION 3.19) +set(CMAKE_CONFIGURATION_TYPES Debug Release) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) - -# Match GMod and our prebuilt libraries -set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") - -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/bin CACHE PATH "..." FORCE) -endif() - -if(WIN32) - if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") - set(GAME_BIN_DIR .) - else() - set(GAME_BIN_DIR win64) - endif() -elseif(UNIX AND NOT APPLE) - set(GAME_BIN_DIR linux64) -else() - message(FATAL_ERROR "No GAME_DIR definition") +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15") + +project(gmod_html) + +set_property(GLOBAL PROPERTY OS_FOLDERS ON) + +set(CEF_VERSION "124.3.5+gff7dcd8+chromium-124.0.6367.119") + +if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") + if("${PROJECT_ARCH}" STREQUAL "arm64") + set(CEF_PLATFORM "macosarm64") + else() + set(CEF_PLATFORM "macosx64") + endif() +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + if(CMAKE_SIZEOF_VOID_P MATCHES 8) + set(CEF_PLATFORM "linux64") + else() + set(CEF_PLATFORM "linux32") + endif() +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") + if(CMAKE_SIZEOF_VOID_P MATCHES 8) + set(CEF_PLATFORM "windows64") + else() + set(CEF_PLATFORM "windows32") + endif() endif() -# Kind of lame but I'm having PDB timestamp mismatches -set (CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin/${GAME_BIN_DIR}) -set (CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin/${GAME_BIN_DIR}) -set (CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin/${GAME_BIN_DIR}) -set (CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin/${GAME_BIN_DIR}) +set(CEF_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/cef3/cef_binary_${CEF_VERSION}_${CEF_PLATFORM}") +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CEF_ROOT}/cmake") -# ImGui -add_subdirectory(thirdparty/imgui-1.74) +find_package(CEF REQUIRED) +add_subdirectory(thirdparty/glad EXCLUDE_FROM_ALL) +add_subdirectory(thirdparty/imgui-1.74 EXCLUDE_FROM_ALL) -# glad -add_subdirectory(thirdparty/glad) - -# GLFW set(GLFW_INSTALL OFF CACHE BOOL "" FORCE) set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) -add_subdirectory(thirdparty/glfw-3.3.2) +add_subdirectory(thirdparty/glfw-3.3.2 EXCLUDE_FROM_ALL) -# Chromium Project -if(WIN32) - if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") - set(CEF_PATH ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/cef3/cef_binary_79.1.31+gfc9ef34+chromium-79.0.3945.117_windows32) - else() - set(CEF_PATH ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/cef3/cef_binary_79.1.31+gfc9ef34+chromium-79.0.3945.117_windows64) - endif() -elseif(UNIX AND NOT APPLE) - set(CEF_PATH ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/cef3/cef_binary_79.1.31+gfc9ef34+chromium-79.0.3945.117_linux64) - add_definitions(-DPOSIX -DLINUX) +if(GEN_NINJA OR GEN_MAKEFILES) + set(CEF_TARGET_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/out/${CMAKE_BUILD_TYPE}/${CEF_PLATFORM}") + set(INSTALL_OUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/dist/${CEF_PLATFORM}-${CMAKE_BUILD_TYPE}") else() - message(FATAL_ERROR "No CEF_PATH") + set(CEF_TARGET_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/out/$/${CEF_PLATFORM}") + set(INSTALL_OUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/dist/${CEF_PLATFORM}-$") endif() +set(CEF_TARGET_OUT_DIR ${CEF_TARGET_OUT_DIR}/bin/${CEF_PLATFORM}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CEF_TARGET_OUT_DIR}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CEF_TARGET_OUT_DIR}) -add_subdirectory(${CEF_PATH} EXCLUDE_FROM_ALL) +add_subdirectory(${CEF_LIBCEF_DLL_WRAPPER_PATH} libcef_dll_wrapper EXCLUDE_FROM_ALL) -# Chromium Import Lib -add_library(libcef_imp SHARED IMPORTED) +add_subdirectory(${CEF_ROOT}/tests/cefclient EXCLUDE_FROM_ALL) +add_subdirectory(${CEF_ROOT}/tests/cefsimple EXCLUDE_FROM_ALL) +add_subdirectory(${CEF_ROOT}/tests/gtest EXCLUDE_FROM_ALL) +add_subdirectory(${CEF_ROOT}/tests/ceftests EXCLUDE_FROM_ALL) -if(WIN32) - set_target_properties(libcef_imp PROPERTIES IMPORTED_LOCATION ${CEF_PATH}/Release/libcef.dll) - set_target_properties(libcef_imp PROPERTIES IMPORTED_IMPLIB ${CEF_PATH}/Release/libcef.lib) -elseif(UNIX AND NOT APPLE) - set_target_properties(libcef_imp PROPERTIES IMPORTED_LOCATION ${CEF_PATH}/Release/libcef.so) -else() - message(FATAL_ERROR "No libcef_imp import library set") -endif() +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -# Chromium Sandbox -add_library(cef_sandbox STATIC IMPORTED) - -if(WIN32) - set_target_properties(cef_sandbox PROPERTIES IMPORTED_LOCATION ${CEF_PATH}/Release/cef_sandbox.lib) -else() - message(FATAL_ERROR "No cef_sandbox library set") -endif() +set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -# Our projects -# add_subdirectory(example_host) add_subdirectory(html) -add_subdirectory(html_stub) +add_subdirectory(html_stub EXCLUDE_FROM_ALL) add_subdirectory(html_chromium) - -if(UNIX) - add_subdirectory(html_chromium/chromium_process) +add_subdirectory(chromium_process) +add_subdirectory(example_host EXCLUDE_FROM_ALL) +if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + add_subdirectory(gmod_launcher) endif() + +PRINT_CEF_CONFIG() diff --git a/README.md b/README.md index e7d354c..2e6e667 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,70 @@ # GMod HTML This is pretty much just an abstraction layer around The Chromium Embedded Framework (https://bitbucket.org/chromiumembedded/cef) -Note: None of this works with the current release of GMod. You'll have to wait for the full 64-bit release before using these with a live build of GMod. ## Chromium Embedded Framework Binary Distribution -To work with this project you will need a build of the Chromium Embedded Framework. You can download the builds used by Garry's Mod here if you don't want to compile your own: +To work with this project you will need a build of the Chromium Embedded Framework. You can download prebuilt versions of CEF from Spotify's Automated Builder if you don't want to compile your own: -| Platform | URL | -| -------- | --- | -| Windows x86 | https://files.facepunch.com/willox/46c89e07-a257-4988-96e8-81e945284469/cef_binary_79.1.31%2Bgfc9ef34%2Bchromium-79.0.3945.117_windows32.tar.bz2 | -| Windows x64 | https://files.facepunch.com/willox/8750c07c-4888-4a79-8449-a01bd6e851f7/cef_binary_79.1.31%2Bgfc9ef34%2Bchromium-79.0.3945.117_windows64.tar.bz2 | -| Linux x64 | https://files.facepunch.com/willox/3004a977-b227-4a20-bbfb-e698913d8a71/cef_binary_79.1.31%2Bgfc9ef34%2Bchromium-79.0.3945.117_linux64.tar.bz2 | -| macOS x64 | https://files.facepunch.com/willox/7fd05979-e9ad-4b94-8d85-0014bacb170c/cef_binary_79.1.35%2Bgfebbb4a%2Bchromium-79.0.3945.130_macosx64.tar.bz2 | +https://cef-builds.spotifycdn.com/index.html -These belong in the `./thirdparty/cef3/` directory (after extraction.) The paths are currently hardcoded into the root `CMakelists.txt` file. +Everything you need is in the **Standard Distribution**. If you need to debug the CEF part, grab the Symbols too. The extracted binary folder belongs in the `./thirdparty/cef3/` directory. The paths are currently hardcoded into the root `CMakelists.txt` file. + +## Currently supported CEF version +The current version of CEF that's supported by this library is: + +- **124.3.5+gff7dcd8+chromium-124.0.6367.119** + +This is not the only version that could be supported, but it's the version that's currently configured and tested to work. ## Getting started ### Windows #### Requirements -- Visual Studio 2019 -- CMake 3.15 or newer +- Visual Studio 2022 +- CMake 3.19 or newer #### Generating Visual Studio Solution ##### x86 ```bat mkdir build_x86 cd build_x86 -cmake -G "Visual Studio 16 2019" -A Win32 .. +cmake -G "Visual Studio 17 2022" -A Win32 .. ``` #### x86-64 ```bat mkdir build_x64 cd build_x64 -cmake -G "Visual Studio 16 2019" -A x64 .. +cmake -G "Visual Studio 17 2022" -A x64 .. ``` -After running either of these sets of commands, you can enter your created directory and open the `gmod-html.sln` solution in Visual Studio. Compiling the `INSTALL` project will place a complete build into `/bin` by default. +After running either of these sets of commands, you can enter your created directory and open the `gmod-html.sln` solution in Visual Studio. Compiling the `INSTALL` project will place a complete build into the `/out` folder by default. ### Linux #### Requirements - A version of GCC/G++ or Clang/Clang++ with C++11 support -- CMake 2.8.7 or newer +- CMake 3.19 or newer #### Compiling ```sh mkdir build cd build -cmake -G "Unix Makefiles" .. +cmake -G "Unix Makefiles" -D CMAKE_BUILD_TYPE=Release .. make && make install ``` -This will place a complete build into `build/bin` by default. +This will place a complete build into the `dist` folder by default. ### macOS -Todo +#### Requirements +- Ninja +- CMake 3.19 or newer + +#### Compiling +```sh +mkdir build +cd build +cmake -G Ninja -D CMAKE_BUILD_TYPE=Release .. +ninja && ninja install +``` + +This will place a complete build into the `dist` folder by default. ## TODO -- Improve the CMake files. We don't use them for GMod builds so they're a bit wonky. -- Get the example_host into workable condition. It's disabled at the moment -- Cleanup. Everything is quite messy. -- macOS. These builds require quite a few unique things so they're not handled by the CMake scripts at all at the moment. It's still technically possible to get the builds working, though. +- Dynamic loading of the HTML implementation. Atm we just use dylib() or LoadLibrary() in each host which is kind of lame. It'd be nice to simplify it. diff --git a/chromium_process/CMakeLists.txt b/chromium_process/CMakeLists.txt new file mode 100644 index 0000000..a52ab7b --- /dev/null +++ b/chromium_process/CMakeLists.txt @@ -0,0 +1,129 @@ + +set(TARGET chromium_process) +set(SOURCES + ChromiumApp.cpp + ChromiumApp.h) +set(RESOURCES ) + +set(SOURCES_LINUX Linux.cpp) +set(SOURCES_MAC macOS.cpp) + +set(SOURCES_WINDOWS Windows.cpp) +set(RESOURCES_WINDOWS resources/win/gmod_icon.rc) + +APPEND_PLATFORM_SOURCES(SOURCES) +APPEND_PLATFORM_SOURCES(RESOURCES) + +if(OS_LINUX OR OS_WINDOWS) + ADD_LOGICAL_TARGET("libcef_lib" "${CEF_LIB_DEBUG}" "${CEF_LIB_RELEASE}") + + if(OS_LINUX) + add_executable(${TARGET} ${SOURCES}) + else() + add_executable(${TARGET} WIN32 ${SOURCES} ${RESOURCES}) + endif() + SET_EXECUTABLE_TARGET_PROPERTIES(${TARGET}) + add_dependencies(${TARGET} libcef_dll_wrapper) + + target_link_libraries(${TARGET} libcef_lib libcef_dll_wrapper ${CEF_STANDARD_LIBS}) + + if(OS_LINUX) + install(TARGETS ${TARGET} DESTINATION "${INSTALL_OUT_DIR}/GarrysMod/bin/${CEF_PLATFORM}") + set_target_properties(${TARGET} PROPERTIES INSTALL_RPATH "$ORIGIN") + set_target_properties(${TARGET} PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE) + else() + if(CMAKE_SIZEOF_VOID_P MATCHES 8) + install(TARGETS ${TARGET} DESTINATION "${INSTALL_OUT_DIR}/GarrysMod/bin/win64") + else() + install(TARGETS ${TARGET} DESTINATION "${INSTALL_OUT_DIR}/GarrysMod/bin") + endif() + + set_target_properties(${TARGET} PROPERTIES OUTPUT_NAME "gmod") + target_link_libraries(${TARGET} + shlwapi + winmm + wsock32 + WS2_32 + comctl32 + rpcrt4 + version + DbgHelp + Psapi + wbemuuid + OleAut32 + SetupAPI + Propsys + Cfgmgr32 + PowrProf + Delayimp.lib) + + if(USE_SANDBOX) + ADD_LOGICAL_TARGET("cef_sandbox_lib" "${CEF_SANDBOX_LIB_DEBUG}" "${CEF_SANDBOX_LIB_RELEASE}") + target_link_libraries(${TARGET} cef_sandbox_lib ${CEF_SANDBOX_STANDARD_LIBS}) + endif() + endif() + + # FIXME: These should be moved to their own target + LIST(REMOVE_ITEM CEF_RESOURCE_FILES "icudtl.dat") + if(OS_LINUX) + COPY_FILES(${TARGET} "icudtl.dat" "${CEF_RESOURCE_DIR}" "${INSTALL_OUT_DIR}/GarrysMod/bin/${CEF_PLATFORM}") + COPY_FILES(${TARGET} "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${INSTALL_OUT_DIR}/GarrysMod/bin/${CEF_PLATFORM}") + COPY_FILES(${TARGET} "${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${INSTALL_OUT_DIR}/GarrysMod/bin/linux32/chromium") + else() + if(CMAKE_SIZEOF_VOID_P MATCHES 8) + COPY_FILES(${TARGET} "icudtl.dat" "${CEF_RESOURCE_DIR}" "${INSTALL_OUT_DIR}/GarrysMod/bin/win64") + COPY_FILES(${TARGET} "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${INSTALL_OUT_DIR}/GarrysMod/bin/win64") + else() + COPY_FILES(${TARGET} "icudtl.dat" "${CEF_RESOURCE_DIR}" "${INSTALL_OUT_DIR}/GarrysMod/bin") + COPY_FILES(${TARGET} "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${INSTALL_OUT_DIR}/GarrysMod/bin") + endif() + COPY_FILES(${TARGET} "${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${INSTALL_OUT_DIR}/GarrysMod/bin/chromium") + endif() +else() + set(HELPER_TARGET "gmod_Helper") + set(HELPER_OUTPUT_NAME "gmod Helper") + add_custom_target(${TARGET} ALL) + + if(USE_SANDBOX) + ADD_LOGICAL_TARGET("cef_sandbox_lib" "${CEF_SANDBOX_LIB_DEBUG}" "${CEF_SANDBOX_LIB_RELEASE}") + endif() + + add_custom_command( + TARGET ${TARGET} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CEF_BINARY_DIR}/Chromium Embedded Framework.framework" + "${INSTALL_OUT_DIR}/GarrysMod_Signed.app/Contents/Frameworks/Chromium Embedded Framework.framework" + VERBATIM) + + foreach(_suffix_list ${CEF_HELPER_APP_SUFFIXES}) + string(REPLACE ":" ";" _suffix_list ${_suffix_list}) + list(GET _suffix_list 0 _name_suffix) + list(GET _suffix_list 1 _target_suffix) + list(GET _suffix_list 2 _plist_suffix) + + set(_helper_target "${HELPER_TARGET}${_target_suffix}") + set(_helper_output_name "${HELPER_OUTPUT_NAME}${_name_suffix}") + + set(_helper_info_plist "${CMAKE_CURRENT_BINARY_DIR}/helper-Info${_target_suffix}.plist") + file(READ "${CMAKE_CURRENT_SOURCE_DIR}/resources/mac/helper-Info.plist" _plist_contents) + string(REPLACE "\${EXECUTABLE_NAME}" "${_helper_output_name}" _plist_contents ${_plist_contents}) + string(REPLACE "\${PRODUCT_NAME}" "${_helper_output_name}" _plist_contents ${_plist_contents}) + string(REPLACE "\${BUNDLE_ID_SUFFIX}" "${_plist_suffix}" _plist_contents ${_plist_contents}) + file(WRITE ${_helper_info_plist} ${_plist_contents}) + + add_executable(${_helper_target} MACOSX_BUNDLE ${SOURCES}) + SET_EXECUTABLE_TARGET_PROPERTIES(${_helper_target}) + add_dependencies(${_helper_target} libcef_dll_wrapper) + target_link_libraries(${_helper_target} libcef_dll_wrapper ${CEF_STANDARD_LIBS}) + set_target_properties(${_helper_target} PROPERTIES + MACOSX_BUNDLE_INFO_PLIST ${_helper_info_plist} + OUTPUT_NAME ${_helper_output_name}) + + install(TARGETS ${_helper_target} DESTINATION ${INSTALL_OUT_DIR}/GarrysMod_Signed.app/Contents/Frameworks) + + if(USE_SANDBOX) + target_link_libraries(${_helper_target} cef_sandbox_lib) + endif() + endforeach() +endif() diff --git a/chromium_process/ChromiumApp.cpp b/chromium_process/ChromiumApp.cpp new file mode 100644 index 0000000..f8e0b7f --- /dev/null +++ b/chromium_process/ChromiumApp.cpp @@ -0,0 +1,500 @@ +#include "ChromiumApp.h" + +static bool V8ValueToCefValue( CefRefPtr outValue, const CefRefPtr& inValue, int depth = 0 ) +{ + if ( depth > 16 ) + return false; + + if ( inValue->IsNull() || inValue->IsUndefined() ) + { + outValue->SetNull(); + } + else if ( inValue->IsBool() ) + { + outValue->SetBool( inValue->GetBoolValue() ); + } + else if ( inValue->IsInt() || inValue->IsUInt() || inValue->IsDouble() ) + { + outValue->SetDouble( inValue->GetDoubleValue() ); + } + else if ( inValue->IsString() ) + { + outValue->SetString( inValue->GetStringValue() ); + } + else if ( inValue->IsArray() ) + { + int arrayLen = inValue->GetArrayLength(); + auto newList = CefListValue::Create(); + newList->SetSize( arrayLen ); + + for ( int i = 0; i < arrayLen; i++ ) + { + auto newValue = CefValue::Create(); + + if ( !V8ValueToCefValue( newValue, inValue->GetValue( i ), depth + 1 ) ) + return false; + + newList->SetValue( i, newValue ); + } + + outValue->SetList( newList ); + } + else if ( inValue->IsObject() ) + { + auto newMap = CefDictionaryValue::Create(); + std::vector keys; + inValue->GetKeys( keys ); + + for ( auto const& key : keys ) + { + auto newValue = CefValue::Create(); + + if ( !V8ValueToCefValue( newValue, inValue->GetValue( key ), depth + 1 ) ) + return false; + + newMap->SetValue( key, newValue ); + } + + outValue->SetDictionary( newMap ); + } + else + { + outValue->SetNull(); + } + + return true; +} + +static bool V8ValuesToCefList( CefRefPtr& outList, const CefV8ValueList& inList ) +{ + outList->SetSize( inList.size() ); + + size_t index = 0; + for ( const auto& x : inList ) + { + auto newValue = CefValue::Create(); + + if ( !V8ValueToCefValue( newValue, x ) ) + return false; + + outList->SetValue( index, newValue ); + index++; + } + + return true; +} + +static bool CefValueToV8Value( CefRefPtr& outValue, const CefRefPtr& inValue, int depth = 0 ) +{ + if ( depth > 16 ) + return false; + + switch ( inValue->GetType() ) + { + case VTYPE_NULL: + outValue = CefV8Value::CreateUndefined(); + break; + case VTYPE_BOOL: + outValue = CefV8Value::CreateBool( inValue->GetBool() ); + break; + case VTYPE_DOUBLE: + outValue = CefV8Value::CreateDouble( inValue->GetDouble() ); + break; + case VTYPE_STRING: + outValue = CefV8Value::CreateString( inValue->GetString() ); + break; + case VTYPE_LIST: + { + auto inList = inValue->GetList(); + int size = static_cast(inList->GetSize()); + outValue = CefV8Value::CreateArray(size); + + for ( int i = 0; i < size; i++ ) + { + CefRefPtr entry; + + if ( !CefValueToV8Value( entry, inList->GetValue( i ), depth + 1 ) ) + return false; + + outValue->SetValue( i, entry ); + } + break; + } + case VTYPE_DICTIONARY: + { + auto inMap = inValue->GetDictionary(); + outValue = CefV8Value::CreateObject( nullptr, nullptr ); + + CefDictionaryValue::KeyList keys; + inMap->GetKeys( keys ); + + for ( const auto& key : keys ) + { + CefRefPtr value; + + if ( !CefValueToV8Value( value, inMap->GetValue( key ), depth + 1 ) ) + return false; + + outValue->SetValue( key, value, V8_PROPERTY_ATTRIBUTE_NONE ); + } + break; + } + default: + outValue = CefV8Value::CreateUndefined(); + break; + } + + return true; +} + +static bool CefListToV8Values( CefV8ValueList& outList, const CefRefPtr inList ) +{ + outList.reserve( inList->GetSize() ); + + for ( size_t i = 0; i < inList->GetSize(); i++ ) + { + CefRefPtr entry; + + if ( !CefValueToV8Value( entry, inList->GetValue( i ) ) ) + return false; + + outList.emplace_back( entry ); + } + + return true; +} + +// +// CefApp interface +// +void ChromiumApp::OnBeforeCommandLineProcessing( const CefString& process_type, CefRefPtr command_line ) +{ + command_line->AppendSwitch( "enable-gpu" ); + command_line->AppendSwitch( "disable-gpu-compositing" ); // NOTE: Enabling GPU Compositing will make OnAcceleratedPaint run instead of OnPaint (CEF must be patched or NOTHING will run!) + command_line->AppendSwitch( "disable-smooth-scrolling" ); +#ifdef _WIN32 + command_line->AppendSwitch( "enable-begin-frame-scheduling" ); +#endif + + // This can interfere with posix signals and break Breakpad +#if defined(__linux__) || defined(__APPLE__) + command_line->AppendSwitch( "disable-in-process-stack-traces" ); + + // Flatpak, AppImage, and Snap break sandboxing + // TODO(winter): It's not ideal to just outright turn off sandboxing...but Steam does it too, so + if (getenv("container") || getenv("APPIMAGE") || getenv("SNAP")) { + command_line->AppendSwitch("no-sandbox"); + } +#endif + +#ifdef __APPLE__ + command_line->AppendSwitch( "use-mock-keychain" ); +#endif + + // https://bitbucket.org/chromiumembedded/cef/issues/2400 + // DXVAVideoDecoding must be disabled for Proton/Wine + // FirstPartySets causes crashing on Chromium 120, also it's an anti-privacy feature + command_line->AppendSwitchWithValue( "disable-features", "TouchpadAndWheelScrollLatching,AsyncWheelEvents,HardwareMediaKeyHandling,DXVAVideoDecoding,FirstPartySets" ); + + // Auto-play media + command_line->AppendSwitchWithValue( "autoplay-policy", "no-user-gesture-required" ); + + // Disable site isolation until we implement passing registered Lua functions between processes + //command_line->AppendSwitch( "disable-site-isolation-trials" ); + + // Enable remote debugging; see also: settings.remote_debugging_port + //command_line->AppendSwitchWithValue( "remote-allow-origins", "http://localhost:9222" ); +} + +void ChromiumApp::OnRegisterCustomSchemes( CefRawPtr registrar ) +{ + // TODO: are these bools what we want them to be? + registrar->AddCustomScheme( "asset", CEF_SCHEME_OPTION_STANDARD | CEF_SCHEME_OPTION_CSP_BYPASSING ); +} + +CefRefPtr ChromiumApp::GetRenderProcessHandler() +{ + return this; +} + +// +// CefBrowserProcessHandler interface +// +bool ChromiumApp::OnAlreadyRunningAppRelaunch( CefRefPtr command_line, const CefString ¤t_directory ) +{ + // See ChromiumSystem::Init, we detect lockfile and handle things there + return true; +} + +// +// CefRenderProcessHandler interface +// +void ChromiumApp::OnContextCreated( CefRefPtr browser, CefRefPtr frame, CefRefPtr context ) +{ + // + // CEF3 only supports implementing CefPrintHandler on Linux, so we've gotta just remove window.print. + // + context->Enter(); + { + context->GetGlobal()->DeleteValue( "print" ); + } + context->Exit(); + + // If this is a web worker, we want nothing to do with it + if ( !browser ) + return; + + for ( auto& pair : m_RegisteredFunctions ) + { + RegisterFunctionInFrame( frame, pair.first, pair.second ); + } +} + +void ChromiumApp::OnContextReleased( CefRefPtr browser, CefRefPtr frame, CefRefPtr context ) +{ + // Do nothing +} + +bool ChromiumApp::OnProcessMessageReceived( CefRefPtr browser, CefRefPtr frame, CefProcessId source_process, CefRefPtr message ) +{ + auto name = message->GetName(); + auto args = message->GetArgumentList(); + + if ( name == "RegisterFunction" ) + { + RegisterFunction( browser, args ); + return true; + } + + if ( name == "ExecuteCallback" ) + { + ExecuteCallback( browser, args ); + return true; + } + + if ( name == "ForgetCallback" ) + { + ForgetCallback( browser, args ); + return true; + } + + if ( name == "ExecuteJavaScript" ) + { + ExecuteJavaScript( browser, args ); + return true; + } + + return false; +} + +// +// CefV8Handler interface +// +bool ChromiumApp::Execute( const CefString& name, CefRefPtr object, const CefV8ValueList& arguments, CefRefPtr& retval, CefString& exception ) +{ + auto context = CefV8Context::GetCurrentContext(); + auto browser = context->GetBrowser(); + + if ( !browser ) + { + // This could happen inside of a web worker (but our functions shouldn't be registered there) + exception = "CefV8Context::GetBrowser == nullptr"; + return true; + } + + // + // We need to rip the objName/funcName out of this function's name + // + std::string objName, funcName; + { + std::string fullName = name.ToString(); + size_t delimPos = fullName.find( '#' ); + + if ( delimPos == std::string::npos ) + { + // Shouldn't happen + exception = "ChromiumApp::Execute couldn't parse this function's name"; + return true; + } + + objName = fullName.substr( 0, delimPos ); + funcName = fullName.substr( delimPos + 1 ); + } + + auto argsList = CefListValue::Create(); + if ( !V8ValuesToCefList( argsList, arguments ) ) + { + exception = "failed to serialize parameters (did you return a deeply nested structure?)"; + return true; + } + + CefRefPtr callback; + + auto message = CefProcessMessage::Create( "ExecuteFunction" ); + auto args = message->GetArgumentList(); + + args->SetString( 0, objName ); + args->SetString( 1, funcName ); + + // No callback = easy mode + if ( arguments.empty() || !arguments.back()->IsFunction() ) + { + args->SetInt( 2, -1 ); // Invalid callback id + args->SetList( 3, argsList ); + } + else + { + // Now register a callback index that Lua will send back to us later + int callbackId = CreateCallback( context, arguments.back() ); + + // We pass argsList to Lua, so pop the callback off of it + argsList->Remove( argsList->GetSize() - 1 ); + + args->SetInt( 2, callbackId ); + args->SetList( 3, argsList ); + } + + browser->GetMainFrame()->SendProcessMessage( PID_BROWSER, message ); + return true; +} + +// + +int ChromiumApp::CreateCallback( CefRefPtr context, CefRefPtr func ) +{ + if ( m_Callbacks.find( m_NextCallbackId ) != m_Callbacks.end() ) + { + // We're overlapping? Probably shouldn't happen + return -1; + } + + int callbackId = m_NextCallbackId; + Callback cb; + cb.Context = context; + cb.Function = func; + + m_Callbacks.emplace( callbackId, cb ); + + m_NextCallbackId++; + if ( m_NextCallbackId >= 16384 ) + m_NextCallbackId = 0; + + return callbackId; +} + +void ChromiumApp::RegisterFunctionInFrame( CefRefPtr frame, const CefString& objName, const CefString& funcName ) +{ + if ( !frame ) + return; + + auto context = frame->GetV8Context(); + + if ( !context ) + return; + + // + // We can only associate one string with a function in CEF. So we'll use "{objName}#{funcName}". + // + CefString fullName; + { + std::stringstream ss; + ss << objName.ToString(); + ss << '#'; + ss << funcName.ToString(); + fullName = CefString( ss.str() ); + } + + context->Enter(); + { + auto window = context->GetGlobal(); + auto obj = window->GetValue( objName ); + + // If our object doesn't exist, create it + if ( !obj || !obj->IsObject() ) + { + obj = CefV8Value::CreateObject( nullptr, nullptr ); + window->SetValue( objName, obj, V8_PROPERTY_ATTRIBUTE_NONE ); + } + + auto func = CefV8Value::CreateFunction( fullName, this ); + obj->SetValue( funcName, func, V8_PROPERTY_ATTRIBUTE_NONE ); + } + context->Exit(); +} + +void ChromiumApp::ExecuteJavaScript( CefRefPtr browser, CefRefPtr args ) +{ + CefString jsUrl = args->GetString( 0 ); + CefString jsSrc = args->GetString( 1 ); + + auto mainFrame = browser->GetMainFrame(); + + if ( !mainFrame || !mainFrame->IsValid() ) + return; + + auto context = mainFrame->GetV8Context(); + + if ( !context || !context->IsValid() ) + return; + + context->Enter(); + { + CefRefPtr retVal; + CefRefPtr retException; + context->Eval( jsSrc, jsUrl, 1, retVal, retException ); + } + context->Exit(); +} + +void ChromiumApp::RegisterFunction( CefRefPtr browser, CefRefPtr args ) +{ + CefString objName = args->GetString( 0 ); + CefString funcName = args->GetString( 1 ); + + // Register this function in any frames that already exist + { + std::vector frames; + browser->GetFrameIdentifiers( frames ); + + for ( auto frameId : frames ) + { + RegisterFunctionInFrame( browser->GetFrameByIdentifier( frameId ), objName, funcName ); + } + } + + m_RegisteredFunctions.emplace_back( std::make_pair( objName, funcName ) ); +} + +void ChromiumApp::ExecuteCallback( CefRefPtr browser, CefRefPtr args ) +{ + int callbackId = args->GetInt( 0 ); + auto it = m_Callbacks.find( callbackId ); + if ( it == m_Callbacks.end() ) + return; + + auto context = it->second.Context; + auto func = it->second.Function; + + m_Callbacks.erase( it ); + + // Context has been destroyed for some reason + if ( !context->IsValid() ) + return; + + auto argList = args->GetList( 1 ); + + context->Enter(); + { + CefV8ValueList arguments; + if ( CefListToV8Values( arguments, argList ) ) + { + func->ExecuteFunction( nullptr, arguments ); + } + } + context->Exit(); +} + +void ChromiumApp::ForgetCallback( CefRefPtr browser, CefRefPtr args ) +{ + m_Callbacks.erase( args->GetInt( 0 ) ); +} diff --git a/chromium_process/ChromiumApp.h b/chromium_process/ChromiumApp.h new file mode 100644 index 0000000..dcc3e52 --- /dev/null +++ b/chromium_process/ChromiumApp.h @@ -0,0 +1,62 @@ +#include "include/cef_app.h" + +#include +#include + +class ChromiumApp + : public CefApp + , public CefBrowserProcessHandler + , public CefRenderProcessHandler + , public CefV8Handler +{ +public: + // + // CefApp interface + // + void OnBeforeCommandLineProcessing( const CefString& process_type, CefRefPtr command_line ) override; + void OnRegisterCustomSchemes( CefRawPtr registrar ) override; + CefRefPtr GetRenderProcessHandler() override; + + // + // CefBrowserProcessHandler interface + // + bool OnAlreadyRunningAppRelaunch( CefRefPtr command_line, const CefString ¤t_directory ) override; + + // + // CefRenderProcessHandler interface + // + void OnContextCreated( CefRefPtr browser, CefRefPtr frame, CefRefPtr context ) override; + void OnContextReleased( CefRefPtr browser, CefRefPtr frame, CefRefPtr context ) override; + bool OnProcessMessageReceived( CefRefPtr browser, CefRefPtr frame, CefProcessId source_process, CefRefPtr message ) override; + + // + // CefV8Handler interface + // + bool Execute( const CefString& name, CefRefPtr object, const CefV8ValueList& arguments, CefRefPtr& retval, CefString& exception ) override; + +private: + int CreateCallback( CefRefPtr context, CefRefPtr func ); + void RegisterFunctionInFrame( CefRefPtr frame, const CefString& objName, const CefString& funcName ); + + // Messages from the game process + void ExecuteJavaScript( CefRefPtr browser, CefRefPtr args ); + void RegisterFunction( CefRefPtr browser, CefRefPtr args ); + void ExecuteCallback( CefRefPtr browser, CefRefPtr args ); + void ForgetCallback( CefRefPtr browser, CefRefPtr args ); + +private: + std::vector> m_RegisteredFunctions; + + struct Callback + { + CefRefPtr Context; + CefRefPtr Function; + }; + + std::unordered_map m_Callbacks; + int m_NextCallbackId = 0; + +private: + IMPLEMENT_REFCOUNTING( ChromiumApp ); + +}; diff --git a/chromium_process/Linux.cpp b/chromium_process/Linux.cpp new file mode 100644 index 0000000..275cac0 --- /dev/null +++ b/chromium_process/Linux.cpp @@ -0,0 +1,8 @@ +#include "include/cef_app.h" +#include "ChromiumApp.h" + +int main(int argc, char* argv[]) { + CefMainArgs main_args(argc, argv); + CefRefPtr app(new ChromiumApp()); + return CefExecuteProcess(main_args, app, nullptr); +} diff --git a/chromium_process/Windows.cpp b/chromium_process/Windows.cpp new file mode 100644 index 0000000..4b4d718 --- /dev/null +++ b/chromium_process/Windows.cpp @@ -0,0 +1,76 @@ + +#include +#include +#include + +#include +#include +#include + +#if __x86_64__ || _WIN64 + #define ENVIRONMENT64 +#else + #define ENVIRONMENT32 +#endif + +#include "include/cef_app.h" +#include "ChromiumApp.h" + +#ifdef CEF_USE_SANDBOX + #include "include/cef_sandbox_win.h" + + extern "C" + { + __declspec( dllexport ) void* CreateCefSandboxInfo() + { + return cef_sandbox_info_create(); + } + + __declspec( dllexport ) void DestroyCefSandboxInfo( void* info ) + { + cef_sandbox_info_destroy( info ); + } + } +#endif + +typedef int (*LauncherMain_t)(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow); + +int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { + // Check if "--type=" is in the command arguments. If it is, we are a chromium subprocess. + if (strstr(lpCmdLine, "--type=")) { + void* sandbox_info = nullptr; + +#ifdef CEF_USE_SANDBOX + CefScopedSandboxInfo scoped_sandbox; + sandbox_info = scoped_sandbox.sandbox_info(); +#endif + + CefMainArgs main_args(hInstance); + CefRefPtr app(new ChromiumApp()); + + int exit_code = CefExecuteProcess(main_args, app, sandbox_info); + if (exit_code >= 0) { + return exit_code; + } + } + + char executable_path[MAX_PATH] = { 0 }; + GetModuleFileNameA(NULL, executable_path, MAX_PATH); + + std::string::size_type last_slash = std::string(executable_path).find_last_of("\\/"); + std::string executable_dir = std::string(executable_path).substr(0, last_slash); + +#ifdef ENVIRONMENT64 + executable_dir += "\\..\\.."; +#else + executable_dir += "\\.."; + MessageBoxA(NULL, "You may encounter stability issues with GModCEFCodecFix in 32-bit mode. Please launch Garry's Mod in 64-bit mode instead if possible.", "32-bit Warning", 0); +#endif + + _chdir(executable_dir.c_str()); + + // Launch GarrysMod's main function from this process. We needed this so the "main" process could provide sandbox information above. + HMODULE hLauncher = LoadLibraryA("launcher.dll"); + LauncherMain_t mainFn = (LauncherMain_t)(GetProcAddress(hLauncher, "LauncherMain")); + return mainFn(hInstance, hPrevInstance, lpCmdLine, nCmdShow); +} diff --git a/chromium_process/macOS.cpp b/chromium_process/macOS.cpp new file mode 100644 index 0000000..d8f5f12 --- /dev/null +++ b/chromium_process/macOS.cpp @@ -0,0 +1,24 @@ +#include "include/cef_app.h" +#include "include/wrapper/cef_library_loader.h" + +#if defined(CEF_USE_SANDBOX) +#include "include/cef_sandbox_mac.h" +#endif + +#include "ChromiumApp.h" + +int main(int argc, char* argv[]) { +#if defined(CEF_USE_SANDBOX) + CefScopedSandboxContext sandbox_context; + if (!sandbox_context.Initialize(argc, argv)) + return 1; +#endif + + CefScopedLibraryLoader library_loader; + if (!library_loader.LoadInHelper()) + return 1; + + CefMainArgs main_args(argc, argv); + CefRefPtr app(new ChromiumApp()); + return CefExecuteProcess(main_args, app, nullptr); +} diff --git a/chromium_process/resources/mac/helper-Info.plist b/chromium_process/resources/mac/helper-Info.plist new file mode 100644 index 0000000..e4d500e --- /dev/null +++ b/chromium_process/resources/mac/helper-Info.plist @@ -0,0 +1,33 @@ + + + + + CFBundleDisplayName + ${EXECUTABLE_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.facepunch.garrysmod_webhelper${BUNDLE_ID_SUFFIX} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + LSEnvironment + + MallocNanoZone + 0 + + LSFileQuarantineEnabled + + LSMinimumSystemVersion + 10.11.0 + LSUIElement + 1 + NSSupportsAutomaticGraphicsSwitching + + + \ No newline at end of file diff --git a/chromium_process/resources/win/gmod.ico b/chromium_process/resources/win/gmod.ico new file mode 100644 index 0000000..adc36d9 Binary files /dev/null and b/chromium_process/resources/win/gmod.ico differ diff --git a/chromium_process/resources/win/gmod_icon.rc b/chromium_process/resources/win/gmod_icon.rc new file mode 100644 index 0000000..f8574f0 --- /dev/null +++ b/chromium_process/resources/win/gmod_icon.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "gmod.ico" diff --git a/example_host/CMakeLists.txt b/example_host/CMakeLists.txt index 137e5ff..3d5f6f4 100644 --- a/example_host/CMakeLists.txt +++ b/example_host/CMakeLists.txt @@ -1,3 +1,5 @@ + +set(TARGET example_host) set(SOURCES Main.cpp Window.cpp @@ -9,36 +11,13 @@ set(SOURCES HtmlPanel.cpp HtmlPanel.h) -if(WIN32) - set(SOURCES - ${SOURCES} - ../html_chromium/chromium_process/ChromiumApp.cpp - ../html_chromium/chromium_process/ChromiumApp.h - ../html_chromium/chromium_process/Windows.cpp) -endif() - -# Lame -include_directories(${CEF_PATH}) - -add_executable(example_host WIN32 ${SOURCES}) -target_link_libraries(example_host glfw glad imgui html libcef_imp libcef_dll_wrapper) -target_link_libraries(example_host optimized cef_sandbox) +add_executable(${TARGET} ${SOURCES}) +SET_EXECUTABLE_TARGET_PROPERTIES(${TARGET}) +add_dependencies(${TARGET} html_chromium html chromium_process glfw glad imgui) +target_compile_definitions(${TARGET} PRIVATE IMGUI_IMPL_OPENGL_LOADER_GLAD) +target_link_libraries(${TARGET} glfw glad imgui html) -if(WIN32) - target_link_libraries(example_host - shlwapi - winmm - wsock32 - comctl32 - rpcrt4 - version - DbgHelp - Psapi - wbemuuid - OleAut32 - SetupAPI - Propsys - Cfgmgr32 - PowrProf - Delayimp.lib) +if(OS_LINUX) + set_target_properties(${TARGET} PROPERTIES INSTALL_RPATH "$ORIGIN") + set_target_properties(${TARGET} PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE) endif() diff --git a/example_host/HtmlPanel.cpp b/example_host/HtmlPanel.cpp index a0fa664..b5fdbd8 100644 --- a/example_host/HtmlPanel.cpp +++ b/example_host/HtmlPanel.cpp @@ -1,3 +1,4 @@ + #include #include "HtmlPanel.h" @@ -48,14 +49,14 @@ void HtmlPanel::UpdateTexture() glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexImage2D( GL_TEXTURE_2D, 0, GL_BGRA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, data ); - m_Texture = reinterpret_cast( texture ); + m_Texture = reinterpret_cast( static_cast( texture ) ); m_TextureWidth = width; m_TextureHeight = height; } else { // Update current texture - glBindTexture( GL_TEXTURE_2D, reinterpret_cast( m_Texture ) ); + glBindTexture( GL_TEXTURE_2D, static_cast( reinterpret_cast( m_Texture ) ) ); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, data ); } diff --git a/example_host/HtmlPanel.h b/example_host/HtmlPanel.h index 6004784..90d1ef9 100644 --- a/example_host/HtmlPanel.h +++ b/example_host/HtmlPanel.h @@ -1,3 +1,4 @@ + #pragma once #include "html/IHtmlSystem.h" @@ -42,4 +43,4 @@ class HtmlPanel : public IHtmlClientListener ImTextureID m_Texture; int m_TextureWidth; int m_TextureHeight; -}; \ No newline at end of file +}; diff --git a/example_host/HtmlResourceHandler.cpp b/example_host/HtmlResourceHandler.cpp index 78b3ced..5997d2e 100644 --- a/example_host/HtmlResourceHandler.cpp +++ b/example_host/HtmlResourceHandler.cpp @@ -1,3 +1,4 @@ + #include "HtmlResourceHandler.h" #include @@ -29,4 +30,4 @@ void HtmlResourceHandler::ReadData( HtmlResource* resource, char* pDestination, void HtmlResourceHandler::Message( const char* data ) { std::cout << data << std::endl; -} \ No newline at end of file +} diff --git a/example_host/HtmlResourceHandler.h b/example_host/HtmlResourceHandler.h index 7f37a57..e093be9 100644 --- a/example_host/HtmlResourceHandler.h +++ b/example_host/HtmlResourceHandler.h @@ -1,3 +1,4 @@ + #pragma once #include "html/IHtmlResourceHandler.h" @@ -12,4 +13,3 @@ class HtmlResourceHandler : public IHtmlResourceHandler void Message( const char* data ) override; }; - diff --git a/example_host/HtmlSystemLoader.cpp b/example_host/HtmlSystemLoader.cpp index 7241a95..99eda94 100644 --- a/example_host/HtmlSystemLoader.cpp +++ b/example_host/HtmlSystemLoader.cpp @@ -1,7 +1,19 @@ -#include -#include + #include +#ifdef _WIN32 + #include + #include +#endif + +#ifdef __linux__ + #include + #include + #include + #include + #include +#endif + #include "HtmlSystemLoader.h" #include "HtmlResourceHandler.h" @@ -34,6 +46,25 @@ bool HtmlSystem_Init() finalPath.append( "/../../" ); // This has to point to where our 'hl2.exe' would live return g_pHtmlSystem->Init( finalPath.c_str(), &g_ResourceHandler ); +#elif __linux__ + void* library = dlopen( "html_chromium_client.so", RTLD_LAZY ); + + if ( library == nullptr ) + return false; + + IHtmlSystem** ppHtmlSystem = reinterpret_cast( dlsym( library, "g_pHtmlSystem" ) ); + + if ( ppHtmlSystem == nullptr || *ppHtmlSystem == nullptr ) + return false; + + g_pHtmlSystem = *ppHtmlSystem; + + char pPath[PATH_MAX] = { 0 }; + if ( getcwd( pPath, sizeof( pPath ) ) == NULL ) + return false; + strcat(pPath, "/../../"); + + return g_pHtmlSystem->Init( canonicalize_file_name(pPath), &g_ResourceHandler ); #else #error #endif diff --git a/example_host/Main.cpp b/example_host/Main.cpp index 0632f26..74abcad 100644 --- a/example_host/Main.cpp +++ b/example_host/Main.cpp @@ -1,43 +1,31 @@ -#include -#include -#include "Window.h" -#include "HtmlSystemLoader.h" -#include "HtmlPanel.h" +#include +#include -#include "glad/glad.h" -#include "GLFW/glfw3.h" +#ifndef IMGUI_IMPL_OPENGL_LOADER_GLAD +#define IMGUI_IMPL_OPENGL_LOADER_GLAD +#endif #include "imgui.h" #include "imgui_impl_glfw.h" #include "imgui_impl_opengl3.h" -#if defined( _WIN32 ) && defined( NDEBUG ) - #include "include/cef_sandbox_win.h" +#include "glad/glad.h" +#include "GLFW/glfw3.h" - extern "C" - { - __declspec( dllexport ) void* CreateCefSandboxInfo() - { - return cef_sandbox_info_create(); - } +#include "Window.h" +#include "HtmlSystemLoader.h" +#include "HtmlPanel.h" - __declspec( dllexport ) void DestroyCefSandboxInfo( void* info ) - { - cef_sandbox_info_destroy( info ); - } - } -#endif static void glfw_error_callback( int error, const char* description ) { fprintf( stderr, "Glfw Error %d: %s\n", error, description ); } -int WINAPI WinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow ) -{ - // Sub-process #ifdef _WIN32 +int WINAPI WinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow ) { + if ( strstr( lpCmdLine, "--type=" ) ) { int ChromiumMain( HINSTANCE hInstance ); @@ -49,20 +37,26 @@ int WINAPI WinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, return exit_code; } } +#else +int main( int argc, char** argv ) { #endif - if ( !HtmlSystem_Init() ) + if ( !HtmlSystem_Init() ) { + std::cout << "Failed to initialize HtmlSystem" << std::endl; return -1; + } glfwSetErrorCallback( glfw_error_callback ); - - if ( !glfwInit() ) + if ( !glfwInit() ){ + std::cout << "Failed to initialize GLFW" << std::endl; return -1; + } { auto window = Window::Create( "Example Host" ); if ( !window ) { + std::cout << "Failed to create window" << std::endl; glfwTerminate(); return -1; } @@ -73,25 +67,30 @@ int WINAPI WinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGui::StyleColorsDark(); + ImGui::GetIO().IniFilename = nullptr; + ImGui_ImplGlfw_InitForOpenGL( window->GetInternal(), true ); ImGui_ImplOpenGL3_Init( "#version 130" ); - ImGui::GetIO().IniFilename = nullptr; while ( !window->ShouldClose() ) { HtmlSystem_Tick(); window->PollEvents(); - glClearColor( 0.f, 0.f, 0.f, 1.f ); - glClear( GL_COLOR_BUFFER_BIT ); - ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); panel->Render(); - + ImGui::Render(); + + int display_w, display_h; + glfwGetFramebufferSize( window->GetInternal(), &display_w, &display_h ); + glViewport( 0, 0, display_w, display_h ); + glClearColor( 0.f, 0.f, 0.f, 1.f ); + glClear( GL_COLOR_BUFFER_BIT ); + ImGui_ImplOpenGL3_RenderDrawData( ImGui::GetDrawData() ); window->SwapBuffers(); @@ -104,5 +103,6 @@ int WINAPI WinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, glfwTerminate(); HtmlSystem_Shutdown(); + return 0; } \ No newline at end of file diff --git a/example_host/Window.cpp b/example_host/Window.cpp index cb736a5..570c386 100644 --- a/example_host/Window.cpp +++ b/example_host/Window.cpp @@ -1,9 +1,12 @@ -#include #include "Window.h" #include "glad/glad.h" #include "GLFW/glfw3.h" +#ifdef _WIN32 + #include +#endif + static void glfw_framebuffersize_callback( GLFWwindow* glfwWindow, int width, int height ) { diff --git a/example_host/Window.h b/example_host/Window.h index 1c959af..d06474a 100644 --- a/example_host/Window.h +++ b/example_host/Window.h @@ -1,3 +1,4 @@ + #pragma once #include @@ -36,4 +37,4 @@ class Window public: void OnFramebufferResized( int width, int height ); -}; \ No newline at end of file +}; diff --git a/gmod_launcher/CMakeLists.txt b/gmod_launcher/CMakeLists.txt new file mode 100644 index 0000000..c09d0dd --- /dev/null +++ b/gmod_launcher/CMakeLists.txt @@ -0,0 +1,13 @@ + +set(TARGET gmod_launcher) +set(SOURCES + Linux.cpp) + +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--export-dynamic") # used for exposing the "has_namespace_support" variable for dlsym +add_executable(${TARGET} ${SOURCES}) +target_link_libraries(${TARGET} ${CMAKE_DL_LIBS}) + +install(TARGETS ${TARGET} DESTINATION "${INSTALL_OUT_DIR}/GarrysMod/bin/${CEF_PLATFORM}") +set_target_properties(${TARGET} PROPERTIES INSTALL_RPATH "$ORIGIN") +set_target_properties(${TARGET} PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE) +set_target_properties(${TARGET} PROPERTIES OUTPUT_NAME "gmod") diff --git a/gmod_launcher/Linux.cpp b/gmod_launcher/Linux.cpp new file mode 100644 index 0000000..13c13c2 --- /dev/null +++ b/gmod_launcher/Linux.cpp @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define REALPATH_BUF_SIZE 4096 +typedef int (*LauncherMain_t)(int argc, char **argv); + +char has_namespace_support = 0x0; + +void calc_has_namespace_support() +{ + __pid_t clonedProcessPid; + __pid_t stoppedProcessPid; + int unshareSuccessOrFail; + int statusCode; + + clonedProcessPid = fork(); + if (clonedProcessPid == -1) { + puts("fork failed... assuming unprivileged usernamespaces are disabled"); + } else { + if (clonedProcessPid == 0) { + unshareSuccessOrFail = unshare(0x10000000); + + if (unshareSuccessOrFail != -1) { + exit(0); + } + + fprintf(stderr, "unshare(CLONE_NEWUSER) failed, unprivileged usernamespaces are probably disabled\n"); + exit(1); + } + + stoppedProcessPid = waitpid(clonedProcessPid, &statusCode, 0); + if (clonedProcessPid == stoppedProcessPid) { + has_namespace_support = statusCode == '\0'; + return; + } + + puts("waitpid failed... assuming unprivileged usernamespaces disabled"); + } + + return; +} + +int main(int argc, char *argv[]) +{ + char realPathOut[4104]; + char *unused; + + if (getenv("container") || getenv("APPIMAGE") || getenv("SNAP")) { + printf("Container detected, overriding \"has_namespace_support\"...\n"); + has_namespace_support = 1; + } else { + calc_has_namespace_support(); + } + + memset(realPathOut, 0, REALPATH_BUF_SIZE); + realpath("/proc/self/exe", realPathOut); + unused = strrchr(realPathOut, '/'); + + if (unused != (char *)0x0) { + *unused = '\0'; + unused = strrchr(realPathOut, '/'); + + if (unused != (char *)0x0) { + *unused = '\0'; + unused = strrchr(realPathOut, '/'); + + if (unused != (char *)0x0) { + *unused = '\0'; + } + } + } + + chdir(realPathOut); + + void *launcherHandle = dlopen("launcher_client.so", RTLD_NOW); + if (!launcherHandle) { + char *errorMsg = dlerror(); + fprintf(stderr, "Failed to load the launcher (%s)\n", errorMsg); + return 1; + } else { + LauncherMain_t launcherMainFn = (LauncherMain_t)dlsym(launcherHandle, "LauncherMain"); + + if (!launcherMainFn) { + fprintf(stderr, "Failed to load the launcher entry proc\n"); + return 1; + } else { + return launcherMainFn(argc, argv); + } + + //dlclose(launcherHandle); + } +} diff --git a/gmod_launcher/Windows.cpp b/gmod_launcher/Windows.cpp new file mode 100644 index 0000000..d23603e --- /dev/null +++ b/gmod_launcher/Windows.cpp @@ -0,0 +1,4 @@ +// +// You're looking for chromium_process/Windows.cpp +// It's there because on Windows, gmod_launcher and chromium_process are combined +// diff --git a/html/CMakeLists.txt b/html/CMakeLists.txt index 7713298..83bbbe4 100644 --- a/html/CMakeLists.txt +++ b/html/CMakeLists.txt @@ -8,7 +8,6 @@ set(SOURCES add_library(html INTERFACE) target_include_directories(html INTERFACE ./) -# Let us see the interface in Visual Studio if(MSVC) add_custom_target(html.interface SOURCES ${SOURCES}) -endif() \ No newline at end of file +endif() diff --git a/html/html/IHtmlClient.h b/html/html/IHtmlClient.h index 9abfc0e..759a0b6 100644 --- a/html/html/IHtmlClient.h +++ b/html/html/IHtmlClient.h @@ -11,13 +11,13 @@ class IHtmlClient enum class EventModifiers : int { None = 0, - Shift = 1 << 0, - Control = 1 << 1, - Alt = 1 << 2, - LeftMouse = 1 << 3, - MiddleMouse = 1 << 4, - RightMouse = 1 << 5, - OSX_Cmd = 1 << 6, + Shift = (1 << 0), + Control = (1 << 1), + Alt = (1 << 2), + LeftMouse = (1 << 3), + MiddleMouse = (1 << 4), + RightMouse = (1 << 5), + OSX_Cmd = (1 << 6), }; struct KeyEvent @@ -34,9 +34,10 @@ class IHtmlClient union { unsigned short key_char; // Type::KeyChar - int windows_key_code; // Type::KeyDown / Type::KeyUp; + int windows_key_code; // Type::KeyDown / Type::KeyUp }; + // TODO: Fix this Facepunch! See ChromiumBrowser::SendKeyEvent #ifndef _WIN32 int native_key_code; #endif @@ -95,4 +96,4 @@ class IHtmlClient virtual bool LockImageData() = 0; virtual void UnlockImageData() = 0; virtual const unsigned char* GetImageData( int& imageWide, int& imageTall ) = 0; -}; \ No newline at end of file +}; diff --git a/html/html/IHtmlClientListener.h b/html/html/IHtmlClientListener.h index 44feef3..2dca250 100644 --- a/html/html/IHtmlClientListener.h +++ b/html/html/IHtmlClientListener.h @@ -40,4 +40,4 @@ class IHtmlClientListener // The input and output JSValue instances should be arrays!!! virtual JSValue OnJavaScriptCall( const char* objName, const char* funcName, const JSValue& params ) = 0; -}; \ No newline at end of file +}; diff --git a/html/html/IHtmlResourceHandler.h b/html/html/IHtmlResourceHandler.h index 2a21331..6a4523a 100644 --- a/html/html/IHtmlResourceHandler.h +++ b/html/html/IHtmlResourceHandler.h @@ -20,4 +20,4 @@ class IHtmlResourceHandler // This doesn't belong here at all virtual void Message( const char* data ) = 0; -}; \ No newline at end of file +}; diff --git a/html/html/IHtmlSystem.h b/html/html/IHtmlSystem.h index 336760f..60c07d2 100644 --- a/html/html/IHtmlSystem.h +++ b/html/html/IHtmlSystem.h @@ -43,6 +43,3 @@ class IHtmlSystem #else #error HTMLSYSTEM_EXPORT not defined for platform #endif - - -// Aaaaaaaaaaa \ No newline at end of file diff --git a/html/html/JSValue.h b/html/html/JSValue.h index 9c48caa..20ba20f 100644 --- a/html/html/JSValue.h +++ b/html/html/JSValue.h @@ -50,6 +50,7 @@ class JSValue JSValue() : _type( Type::Undefined ) + , _bool( false ) {} JSValue( bool value ) @@ -79,6 +80,7 @@ class JSValue JSValue( const JSValue& other ) : _type( other._type ) + , _bool( false ) { switch ( _type ) { @@ -104,6 +106,7 @@ class JSValue JSValue( JSValue&& other ) noexcept : _type( other._type ) + , _bool(false) { switch ( _type ) { @@ -237,17 +240,17 @@ class JSValue // - const bool HashMap_Begin( const char*& pKey, size_t& keySize, const JSValue*& pValue ) const + bool HashMap_Begin( const char*& pKey, size_t& keySize, const JSValue*& pValue ) const { return _pHashMap->Begin( pKey, keySize, pValue ); } - const bool HashMap_Next( const char*& pKey, size_t& keySize, const JSValue*& pValue ) const + bool HashMap_Next( const char*& pKey, size_t& keySize, const JSValue*& pValue ) const { return _pHashMap->Next( pKey, keySize, pValue ); } - const size_t HashMap_Size() const + size_t HashMap_Size() const { return _pHashMap->Size(); } @@ -291,4 +294,3 @@ class JSValue IHashMap* _pHashMap; // Type::HashMap }; }; - diff --git a/html_chromium/CMakeLists.txt b/html_chromium/CMakeLists.txt index 6d0a958..27dd8c5 100644 --- a/html_chromium/CMakeLists.txt +++ b/html_chromium/CMakeLists.txt @@ -1,43 +1,13 @@ -if(WIN32) - set(BINARIES - ${CEF_PATH}/Release/chrome_elf.dll - ${CEF_PATH}/Release/d3dcompiler_47.dll - ${CEF_PATH}/Release/libcef.dll - ${CEF_PATH}/Release/libEGL.dll - ${CEF_PATH}/Release/libGLESv2.dll - ${CEF_PATH}/Release/natives_blob.bin - ${CEF_PATH}/Release/snapshot_blob.bin - ${CEF_PATH}/Release/v8_context_snapshot.bin - ${CEF_PATH}/Resources/icudtl.dat) -elseif(UNIX AND NOT APPLE) - set(BINARIES - ${CEF_PATH}/Release/chrome-sandbox - ${CEF_PATH}/Release/libcef.so - ${CEF_PATH}/Release/libEGL.so - ${CEF_PATH}/Release/libGLESv2.so - ${CEF_PATH}/Release/natives_blob.bin - ${CEF_PATH}/Release/snapshot_blob.bin - ${CEF_PATH}/Release/v8_context_snapshot.bin - ${CEF_PATH}/Resources/icudtl.dat) -else() - message(FATAL_ERROR "No IMPORTED_LOCATION for libcef") -endif() - -set(RESOURCES - ${CEF_PATH}/Resources/cef.pak - ${CEF_PATH}/Resources/cef_100_percent.pak - ${CEF_PATH}/Resources/cef_200_percent.pak - ${CEF_PATH}/Resources/cef_extensions.pak - ${CEF_PATH}/Resources/devtools_resources.pak) - -install(FILES ${BINARIES} DESTINATION ${GAME_BIN_DIR}) -install(FILES ${RESOURCES} DESTINATION chromium) -install(DIRECTORY ${CEF_PATH}/Resources/locales DESTINATION chromium) -# Actual lib -set(SOURCES +set(TARGET html_chromium) +set(SOURCES cef_end.h cef_start.h + chromium/dom_code_data.inc + chromium/dom_code.h + chromium/keycode_converter.cc + chromium/keycode_converter.h + DomCodeConverter.h ChromiumBrowser.cpp ChromiumBrowser.h ChromiumClient.cpp @@ -53,9 +23,38 @@ set(SOURCES ResourceHandler.cpp ResourceHandler.h) -# Lame -include_directories(${CEF_PATH}) +if(OS_LINUX OR OS_WINDOWS) + ADD_LOGICAL_TARGET("libcef_lib" "${CEF_LIB_DEBUG}" "${CEF_LIB_RELEASE}") +endif() + +add_library(${TARGET} SHARED ${SOURCES}) +SET_LIBRARY_TARGET_PROPERTIES(${TARGET}) +add_dependencies(${TARGET} html libcef_dll_wrapper) + +if(OS_MAC) + target_link_libraries(${TARGET} html libcef_dll_wrapper ${CEF_STANDARD_LIBS}) +else() + target_link_libraries(${TARGET} html libcef_lib libcef_dll_wrapper ${CEF_STANDARD_LIBS}) +endif() + +if(OS_WINDOWS AND USE_SANDBOX) + ADD_LOGICAL_TARGET("cef_sandbox_lib" "${CEF_SANDBOX_LIB_DEBUG}" "${CEF_SANDBOX_LIB_RELEASE}") + target_link_libraries(${TARGET} cef_sandbox_lib ${CEF_SANDBOX_STANDARD_LIBS}) +endif() -add_library(html_chromium SHARED ${SOURCES}) -target_link_libraries(html_chromium html libcef_imp libcef_dll_wrapper) -SET_TARGET_PROPERTIES(html_chromium PROPERTIES PREFIX "") +set_target_properties(${TARGET} PROPERTIES PREFIX "") +if(OS_LINUX) + set_target_properties(${TARGET} PROPERTIES OUTPUT_NAME "html_chromium_client") +endif() + +if(OS_WINDOWS) + if(CMAKE_SIZEOF_VOID_P MATCHES 8) + install(TARGETS ${TARGET} DESTINATION "${INSTALL_OUT_DIR}/GarrysMod/bin/win64") + else() + install(TARGETS ${TARGET} DESTINATION "${INSTALL_OUT_DIR}/GarrysMod/bin") + endif() +elseif(OS_LINUX) + install(TARGETS ${TARGET} DESTINATION "${INSTALL_OUT_DIR}/GarrysMod/bin/${CEF_PLATFORM}") +elseif(OS_MAC) + install(TARGETS ${TARGET} DESTINATION "${INSTALL_OUT_DIR}/GarrysMod_Signed.app/Contents/MacOS") +endif() diff --git a/html_chromium/ChromiumBrowser.cpp b/html_chromium/ChromiumBrowser.cpp index 86c123c..0d70057 100644 --- a/html_chromium/ChromiumBrowser.cpp +++ b/html_chromium/ChromiumBrowser.cpp @@ -1,4 +1,9 @@ -#include "ChromiumBrowser.h" + +#include +//#include +//#include + +#include "ChromiumBrowser.h" #include "html/IHtmlClient.h" #include "JSObjects.h" @@ -6,6 +11,21 @@ #include "cef_start.h" #include "include/cef_parser.h" #include "cef_end.h" +#include "chromium/dom_code.h" +#include "chromium/keycode_converter.h" +#include "DomCodeConverter.h" + +#ifdef _WIN32 + #include +#endif +#ifdef __linux__ + #include + #include +#endif +#ifdef __APPLE__ + #include + #include +#endif static bool CefValueToJSValue( JSValue& outValue, CefRefPtr inValue, int depth = 0 ) { @@ -183,27 +203,37 @@ static bool JSValuesToCefList( CefRefPtr outList, const std::vecto static int GetModifiers( const IHtmlClient::EventModifiers modifiers ) { - int gameModifiers = static_cast( modifiers ); + int gameModifiers = static_cast(modifiers); int chromiumModifiers = 0; - if ( gameModifiers & static_cast( IHtmlClient::EventModifiers::Shift ) ) + if (gameModifiers & static_cast(IHtmlClient::EventModifiers::Shift)) { chromiumModifiers |= EVENTFLAG_SHIFT_DOWN; + } - if ( gameModifiers & static_cast( IHtmlClient::EventModifiers::Control ) ) + if (gameModifiers & static_cast(IHtmlClient::EventModifiers::Control)) { chromiumModifiers |= EVENTFLAG_CONTROL_DOWN; + } - if ( gameModifiers & static_cast( IHtmlClient::EventModifiers::Alt ) ) + if (gameModifiers & static_cast(IHtmlClient::EventModifiers::Alt)) { chromiumModifiers |= EVENTFLAG_ALT_DOWN; + } - if ( gameModifiers & static_cast( IHtmlClient::EventModifiers::LeftMouse ) ) + if (gameModifiers & static_cast(IHtmlClient::EventModifiers::LeftMouse)) { chromiumModifiers |= EVENTFLAG_LEFT_MOUSE_BUTTON; + } - if ( gameModifiers & static_cast( IHtmlClient::EventModifiers::MiddleMouse ) ) + if (gameModifiers & static_cast(IHtmlClient::EventModifiers::MiddleMouse)) { chromiumModifiers |= EVENTFLAG_MIDDLE_MOUSE_BUTTON; + } - if ( gameModifiers & static_cast( IHtmlClient::EventModifiers::RightMouse ) ) + if (gameModifiers & static_cast(IHtmlClient::EventModifiers::RightMouse)) { chromiumModifiers |= EVENTFLAG_RIGHT_MOUSE_BUTTON; + } + if (gameModifiers & static_cast(IHtmlClient::EventModifiers::OSX_Cmd)) { + chromiumModifiers |= EVENTFLAG_COMMAND_DOWN; + } + return chromiumModifiers; } @@ -214,7 +244,11 @@ ChromiumBrowser::ChromiumBrowser() , m_PopupTall( 0 ) , m_PopupData( nullptr ) , m_OpenLinksExternally( false ) -{} +{ + RunOrDeferForInit([this] { + m_BrowserHost->WasResized(); + }); +} ChromiumBrowser::~ChromiumBrowser() { @@ -243,65 +277,138 @@ void ChromiumBrowser::QueueMessage( MessageQueue::Message&& message ) // void ChromiumBrowser::Close() { - m_BrowserHost->CloseBrowser( true ); + RunOrDeferForInit([this] { + m_BrowserHost->CloseBrowser(true); + }); } void ChromiumBrowser::SetSize( int wide, int tall ) { - m_Wide = wide; - m_Tall = tall; - m_BrowserHost->WasResized(); + RunOrDeferForInit([this, wide, tall] { + m_Wide = wide; + m_Tall = tall; + m_BrowserHost->WasResized(); + }); } void ChromiumBrowser::SetFocused( bool hasFocus ) { - m_BrowserHost->SendFocusEvent( hasFocus ); + RunOrDeferForInit([this, hasFocus] { + m_BrowserHost->SetFocus(hasFocus); + }); } -void ChromiumBrowser::SendKeyEvent( IHtmlClient::KeyEvent keyEvent ) +// Validate against https://dvcs.w3.org/hg/d4e/raw-file/tip/key-event-test.html +void ChromiumBrowser::SendKeyEvent(IHtmlClient::KeyEvent keyEvent) { - CefKeyEvent chromiumKeyEvent; - chromiumKeyEvent.modifiers = GetModifiers( keyEvent.modifiers ); + if (m_BrowserHost == nullptr) { + return; + } - switch ( keyEvent.eventType ) - { - case IHtmlClient::KeyEvent::Type::KeyChar: - chromiumKeyEvent.type = KEYEVENT_CHAR; - chromiumKeyEvent.character = static_cast( keyEvent.key_char ); - chromiumKeyEvent.unmodified_character = static_cast( keyEvent.key_char ); -#ifdef OSX - chromiumKeyEvent.windows_key_code = 0; - chromiumKeyEvent.native_key_code = keyEvent.native_key_code; -#else - chromiumKeyEvent.windows_key_code = static_cast( keyEvent.key_char ); - chromiumKeyEvent.native_key_code = static_cast( keyEvent.key_char ); -#endif - break; - case IHtmlClient::KeyEvent::Type::KeyDown: - chromiumKeyEvent.type = KEYEVENT_KEYDOWN; - chromiumKeyEvent.windows_key_code = keyEvent.windows_key_code; + // TODO/BUG/HACK(winter): Getting wrong windows_key_code for modifiers for some reason... + int modifiers = GetModifiers(keyEvent.modifiers); + + if ((modifiers & EVENTFLAG_SHIFT_DOWN || m_LastKeyEvent.modifiers & EVENTFLAG_SHIFT_DOWN) && keyEvent.windows_key_code == 0xA0) { + keyEvent.windows_key_code = 0x10; + } + if ((modifiers & EVENTFLAG_CONTROL_DOWN || m_LastKeyEvent.modifiers & EVENTFLAG_CONTROL_DOWN) && keyEvent.windows_key_code == 0xA2) { + keyEvent.windows_key_code = 0x11; + } + if ((modifiers & EVENTFLAG_ALT_DOWN || m_LastKeyEvent.modifiers & EVENTFLAG_ALT_DOWN) && keyEvent.windows_key_code == 0x0) { + keyEvent.windows_key_code = 0x12; + } + + // TODO/BUG(winter): IHtmlClient doesn't give us native_key_code on Windows. Facepunch needs to PROPERLY fix this! #ifndef _WIN32 - chromiumKeyEvent.native_key_code = keyEvent.native_key_code; + int native_key_code = keyEvent.native_key_code; #else - chromiumKeyEvent.native_key_code = keyEvent.windows_key_code; + int native_key_code = 0; #endif - break; - case IHtmlClient::KeyEvent::Type::KeyUp: - chromiumKeyEvent.type = KEYEVENT_KEYUP; - chromiumKeyEvent.windows_key_code = keyEvent.windows_key_code; + + // TODO/BUG(winter): IHtmlClient gives us the wrong native_key_code on Linux. Facepunch needs to PROPERLY fix this! + // HACK: Use Chromium's `DomCodeToNativeKeycode` to "generate" the native_key_code + // Will be converted back, in Chromium, with `NativeKeycodeToDomCode`. + bool lastkeydown_null = m_LastKeyEvent.character == 0x0 && m_LastKeyEvent.windows_key_code == 0x0 && m_LastKeyEvent.native_key_code == 0x0; + +#if defined(_WIN32) || defined(__linux__) + ui::DomCode domCode = WindowsKeyCodeToDomCode(keyEvent.windows_key_code, lastkeydown_null); + native_key_code = ui::KeycodeConverter::DomCodeToNativeKeycode(domCode); +#endif + + //LOG(ERROR) << "WINDOWS: " << keyEvent.windows_key_code; + //LOG(ERROR) << "NATIVE: " << native_key_code; + //LOG(ERROR) << "CHAR: " << keyEvent.key_char; + + CefKeyEvent chromiumKeyEvent; + chromiumKeyEvent.modifiers = modifiers; + + switch (keyEvent.eventType) + { + case IHtmlClient::KeyEvent::Type::KeyChar: + //LOG(ERROR) << "KEYEVENT_CHAR"; + + // BUG/HACK: If the last KeyDown was literally nothing, we'll fire a WORKING version of it before continuing + /* + if (lastkeydown_null) { + IHtmlClient::KeyEvent fakeKeyDownEvent{}; + fakeKeyDownEvent.eventType = IHtmlClient::KeyEvent::Type::KeyDown; + fakeKeyDownEvent.modifiers = keyEvent.modifiers; + fakeKeyDownEvent.key_char = keyEvent.key_char; + fakeKeyDownEvent.windows_key_code = keyEvent.windows_key_code; #ifndef _WIN32 - chromiumKeyEvent.native_key_code = keyEvent.native_key_code; + fakeKeyDownEvent.native_key_code = native_key_code; +#endif + + // TODO(winter): Key events trample each other if we effectively send them to CEF at the same time (sleep halts TID_UI!) + //SendKeyEvent(fakeKeyDownEvent); + //this_thread::sleep_for(chrono::milliseconds(100)); + } + */ + + chromiumKeyEvent.type = KEYEVENT_CHAR; + chromiumKeyEvent.character = static_cast(keyEvent.key_char); + chromiumKeyEvent.unmodified_character = static_cast(keyEvent.key_char); +#ifdef __APPLE__ + chromiumKeyEvent.windows_key_code = 0; + chromiumKeyEvent.native_key_code = native_key_code; #else - chromiumKeyEvent.native_key_code = keyEvent.windows_key_code; + chromiumKeyEvent.windows_key_code = keyEvent.windows_key_code; + chromiumKeyEvent.native_key_code = native_key_code; #endif - break; + break; + case IHtmlClient::KeyEvent::Type::KeyDown: + //LOG(ERROR) << "KEYEVENT_RAWKEYDOWN"; + chromiumKeyEvent.type = KEYEVENT_RAWKEYDOWN; // TODO: Fix repeating detection with KEYEVENT_KEYDOWN(?) + chromiumKeyEvent.windows_key_code = keyEvent.windows_key_code; + chromiumKeyEvent.native_key_code = native_key_code; + break; + case IHtmlClient::KeyEvent::Type::KeyUp: + //LOG(ERROR) << "KEYEVENT_KEYUP"; + chromiumKeyEvent.type = KEYEVENT_KEYUP; + chromiumKeyEvent.windows_key_code = keyEvent.windows_key_code; + chromiumKeyEvent.native_key_code = native_key_code; + break; } - m_BrowserHost->SendKeyEvent( chromiumKeyEvent ); + //chromiumKeyEvent.character = 0x0; + //chromiumKeyEvent.unmodified_character = 0x0; + //chromiumKeyEvent.windows_key_code = 0x0; + //chromiumKeyEvent.native_key_code = 0x0; + + m_LastKeyEvent = chromiumKeyEvent; + + // TODO: There has to be *something* to send to CEF for this to make any sense + //if (chromiumKeyEvent.character != 0x0 || chromiumKeyEvent.windows_key_code != 0x0 || chromiumKeyEvent.native_key_code != 0x0) { + m_BrowserHost->SendKeyEvent(chromiumKeyEvent); + //} } void ChromiumBrowser::SendMouseMoveEvent( IHtmlClient::MouseEvent gmodMouseEvent, bool mouseLeave ) { + if (m_BrowserHost == nullptr) { + return; + } + CefMouseEvent mouseEvent; mouseEvent.x = gmodMouseEvent.x; mouseEvent.y = gmodMouseEvent.y; @@ -312,7 +419,11 @@ void ChromiumBrowser::SendMouseMoveEvent( IHtmlClient::MouseEvent gmodMouseEvent void ChromiumBrowser::SendMouseWheelEvent( IHtmlClient::MouseEvent gmodMouseEvent, int deltaX, int deltaY ) { - // Some CEF bug is fucking this up. I don't care much for worrying about it yet + if (m_BrowserHost == nullptr) { + return; + } + + // TODO(willox): Some CEF bug is fucking this up. I don't care much for worrying about it yet CefMouseEvent mouseEvent; mouseEvent.x = gmodMouseEvent.x; mouseEvent.y = gmodMouseEvent.y; @@ -323,6 +434,10 @@ void ChromiumBrowser::SendMouseWheelEvent( IHtmlClient::MouseEvent gmodMouseEven void ChromiumBrowser::SendMouseClickEvent( IHtmlClient::MouseEvent gmodMouseEvent, IHtmlClient::MouseButton gmodButtonType, bool mouseUp, int clickCount ) { + if (m_BrowserHost == nullptr) { + return; + } + CefMouseEvent mouseEvent; mouseEvent.x = gmodMouseEvent.x; mouseEvent.y = gmodMouseEvent.y; @@ -348,63 +463,78 @@ void ChromiumBrowser::SendMouseClickEvent( IHtmlClient::MouseEvent gmodMouseEven void ChromiumBrowser::LoadUrl( const std::string& url ) { - m_Browser->GetMainFrame()->LoadURL( CefString( url ) ); + RunOrDeferForInit([this, url] { + m_Browser->GetMainFrame()->LoadURL(CefString(url)); + }); } void ChromiumBrowser::SetHtml( const std::string& html ) { - // asset://html/?{myhtml} - CefURLParts urlParts; - CefString( &urlParts.scheme ).FromString( "asset" ); - CefString( &urlParts.host ).FromString( "html" ); - CefString( &urlParts.path ).FromString( "/" ); - CefString( &urlParts.query ).FromString( CefBase64Encode( html.c_str(), html.size() ) ); - - CefString url; - if ( !CefCreateURL( urlParts, url ) ) - return; + RunOrDeferForInit([this, html] { + CefURLParts urlParts; + CefString(&urlParts.scheme).FromString("asset"); + CefString(&urlParts.host).FromString("html"); + CefString(&urlParts.path).FromString("/"); + CefString(&urlParts.query).FromString(CefBase64Encode(html.c_str(), html.size())); + + CefString url; + if (!CefCreateURL(urlParts, url)) + return; - m_Browser->GetMainFrame()->LoadURL( url ); + m_Browser->GetMainFrame()->LoadURL(url); + }); } void ChromiumBrowser::Refresh() { - m_Browser->Reload(); + RunOrDeferForInit([this] { + m_Browser->Reload(); + }); } void ChromiumBrowser::Stop() { - m_Browser->StopLoad(); + RunOrDeferForInit([this] { + m_Browser->StopLoad(); + }); } void ChromiumBrowser::GoBack() { - m_Browser->GoBack(); + RunOrDeferForInit([this] { + m_Browser->GoBack(); + }); } void ChromiumBrowser::GoForward() { - m_Browser->GoForward(); + RunOrDeferForInit([this] { + m_Browser->GoForward(); + }); } void ChromiumBrowser::RunJavaScript( const std::string& code ) { - auto message = CefProcessMessage::Create( "ExecuteJavaScript" ); - auto args = message->GetArgumentList(); - - args->SetString( 0, "Lua File" ); - args->SetString( 1, code ); - m_Browser->GetMainFrame()->SendProcessMessage( PID_RENDERER, message ); + RunOrDeferForInit([this, code] { + auto message = CefProcessMessage::Create("ExecuteJavaScript"); + auto args = message->GetArgumentList(); + + args->SetString(0, "Lua File"); + args->SetString(1, code); + m_Browser->GetMainFrame()->SendProcessMessage(PID_RENDERER, message); + }); } void ChromiumBrowser::RegisterJavaScriptFunction( const std::string& objName, const std::string& funcName ) { - auto message = CefProcessMessage::Create( "RegisterFunction" ); - auto args = message->GetArgumentList(); - - args->SetString( 0, objName ); - args->SetString( 1, funcName ); - m_Browser->GetMainFrame()->SendProcessMessage( PID_RENDERER, message ); + RunOrDeferForInit([this, objName, funcName] { + auto message = CefProcessMessage::Create("RegisterFunction"); + auto args = message->GetArgumentList(); + + args->SetString(0, objName); + args->SetString(1, funcName); + m_Browser->GetMainFrame()->SendProcessMessage(PID_RENDERER, message); + }); } void ChromiumBrowser::SetOpenLinksExternally( bool openLinksExternally ) @@ -414,18 +544,20 @@ void ChromiumBrowser::SetOpenLinksExternally( bool openLinksExternally ) void ChromiumBrowser::ExecuteCallback( int callbackId, const JSValue& paramsArray ) { - const auto& paramsVector = ( static_cast( paramsArray.GetInternalArray() ) )->GetInternalData(); // spaghetti - auto message = CefProcessMessage::Create( "ExecuteCallback" ); - auto outArgs = message->GetArgumentList(); + RunOrDeferForInit([this, callbackId, paramsArray] { + const auto& paramsVector = (static_cast(paramsArray.GetInternalArray()))->GetInternalData(); // spaghetti + auto message = CefProcessMessage::Create("ExecuteCallback"); + auto outArgs = message->GetArgumentList(); - auto cefArgs = CefListValue::Create(); - if ( !JSValuesToCefList( cefArgs, paramsVector ) ) - return; + auto cefArgs = CefListValue::Create(); + if (!JSValuesToCefList(cefArgs, paramsVector)) + return; - outArgs->SetInt( 0, callbackId ); - outArgs->SetList( 1, cefArgs ); + outArgs->SetInt(0, callbackId); + outArgs->SetList(1, cefArgs); - m_Browser->GetMainFrame()->SendProcessMessage( PID_RENDERER, message ); + m_Browser->GetMainFrame()->SendProcessMessage(PID_RENDERER, message); + }); } // @@ -462,6 +594,12 @@ void ChromiumBrowser::OnAfterCreated( CefRefPtr browser ) { m_Browser = browser; m_BrowserHost = browser->GetHost(); + + for (auto& func : m_Deferred) { + func(); + } + + m_Deferred = {}; } void ChromiumBrowser::OnBeforeClose( CefRefPtr browser ) @@ -489,7 +627,7 @@ bool ChromiumBrowser::OnBeforePopup( CefRefPtr, msg.type = MessageQueue::Type::OnCreateChildView; msg.string1 = sourceUrl.ToString(); msg.string2 = targetUrl.ToString(); - msg.integer = static_cast( targetDisposition == WOD_NEW_POPUP ); + msg.integer = static_cast( targetDisposition == CEF_WOD_NEW_POPUP ); QueueMessage( std::move( msg ) ); // Don't create the popup @@ -566,10 +704,7 @@ void ChromiumBrowser::OnTitleChange( CefRefPtr, const CefString& tit QueueMessage( std::move( msg ) ); } -// -// CefRenderHandler interface -// -void ChromiumBrowser::OnCursorChange( CefRefPtr, CefCursorHandle, CefRenderHandler::CursorType chromeCursor, const CefCursorInfo& ) +bool ChromiumBrowser::OnCursorChange( CefRefPtr browser, CefCursorHandle, cef_cursor_type_t chromeCursor, const CefCursorInfo& ) { using GModCursorType = IHtmlClientListener::CursorType; GModCursorType gmodCursor; @@ -645,9 +780,13 @@ void ChromiumBrowser::OnCursorChange( CefRefPtr, CefCursorHandle, Ce msg.type = MessageQueue::Type::OnCursorChange; msg.integer = static_cast( gmodCursor ); QueueMessage( std::move( msg ) ); -} + return false; +} +// +// CefRenderHandler interface +// void ChromiumBrowser::GetViewRect( CefRefPtr, CefRect& rect ) { rect.x = 0; @@ -683,6 +822,15 @@ void ChromiumBrowser::OnPopupSize( CefRefPtr, const CefRect& rect ) m_PopupY = rect.y; } +bool ShouldFullCopy( const CefRenderHandler::RectList& dirtyRects, int width, int height ) { + int dirty_area = 0; + for (auto &&rect : dirtyRects) { + dirty_area += rect.width * rect.height; + } + // TODO: Find optimal threshold using benchmarking. + return dirty_area > 0.8 * width * height; +} + void ChromiumBrowser::OnPaint( CefRefPtr, CefRenderHandler::PaintElementType type, const CefRenderHandler::RectList& dirtyRects, const void* buffer, int width, int height ) { // @@ -702,26 +850,40 @@ void ChromiumBrowser::OnPaint( CefRefPtr, CefRenderHandler::PaintEle case PET_VIEW: m_ImageData.Lock(); - m_ImageData.SetData( static_cast( buffer ), width, height ); - // Blit our popup over this image - if ( m_PopupWide > 0 && m_PopupTall > 0 ) - { - // Copy row-by-row because the destination pixels may not be contiguous - for ( int SrcY = 0; SrcY < m_PopupTall; SrcY++ ) + if ( m_ImageData.ResizeData( width, height ) || ShouldFullCopy( dirtyRects, width, height ) ) { + // Copy whole buffer over ImageData + m_ImageData.SetData(static_cast(buffer), width, height); + } else { + // Blit the dirty parts of buffer over ImageData + for (auto &&rect : dirtyRects) { - memcpy( &m_ImageData.m_Data[( SrcY + m_PopupY ) * width * 4 + m_PopupX * 4], - &m_PopupData[SrcY * m_PopupWide * 4], - m_PopupWide * 4 ); + if (!rect.IsEmpty()) { + m_ImageData.Blit(static_cast(buffer), rect.x, rect.y, rect.width, rect.height); + } } } - m_ImageData.SetDirty( true ); + // Blit our popup over the ImageData + if ( m_PopupWide > 0 && m_PopupTall > 0 ) + { + m_ImageData.BlitRelative(m_PopupData, m_PopupX, m_PopupY, m_PopupWide, m_PopupTall); + } + m_ImageData.Unlock(); return; } } +void ChromiumBrowser::OnAcceleratedPaint(CefRefPtr browser, CefRenderHandler::PaintElementType type, const CefRenderHandler::RectList& dirtyRects, const CefAcceleratedPaintInfo& info) +{ + // TODO: Implement once fixed for OSR on Viz + // TODO: See ChromiumSystem::CreateClient + // https://bitbucket.org/chromiumembedded/cef/pull-requests/285/reimplement-shared-texture-support-for-viz + + LOG(ERROR) << "ChromiumBrowser::OnAcceleratedPaint"; +} + // // CefRequestHandler interface // @@ -752,23 +914,29 @@ bool ChromiumBrowser::OnBeforeBrowse( CefRefPtr, #ifndef __APPLE__ if ( m_OpenLinksExternally ) { -#if defined( _WIN32 ) - ShellExecute( NULL, "open", request->GetURL().ToString().c_str(), NULL, NULL, SW_SHOWNORMAL ); -#elif defined( __linux__ ) - std::string strUrl = request->GetURL().ToString(); - pid_t pid; - const char* args[3]; - args[0] = "/usr/bin/xdg-open"; - args[1] = strUrl.c_str(); - args[2] = NULL; - pid = fork(); - - if ( pid == 0 ) - { - execvp( args[0], (char* const*) args ); + std::string url = request->GetURL().ToString(); + +#if defined(_WIN32) + ShellExecuteA(NULL, "open", url.c_str(), NULL, NULL, SW_SHOWNORMAL); +#elif defined(__linux__) + pid_t pid = fork(); + if (pid == 0) { + execlp("xdg-open", "xdg-open", url.c_str(), NULL); + exit(0); + } else if (pid < 0) { + LOG(ERROR) << "Failed to open URL in external browser: " << url; + } else { + // TODO: Should we wait for the child process to finish? + waitpid(pid, NULL, 0); + } +#elif defined(__APPLE__) + CFURLRef urlRef = CFURLCreateWithBytes(NULL, (const UInt8 *)url.c_str(), url.length(), kCFStringEncodingUTF8, NULL); + if (urlRef) { + LSOpenCFURLRef(urlRef, NULL); + CFRelease(urlRef); } #else -#error +#error Unsupported platform #endif return true; } @@ -780,7 +948,7 @@ bool ChromiumBrowser::OnBeforeBrowse( CefRefPtr, ChromiumBrowser::ReturnValue ChromiumBrowser::OnBeforeResourceLoad( CefRefPtr, CefRefPtr, CefRefPtr request, - CefRefPtr ) + CefRefPtr ) { CefURLParts urlParts; if ( !CefParseURL( request->GetURL(), urlParts ) ) @@ -796,6 +964,11 @@ ChromiumBrowser::ReturnValue ChromiumBrowser::OnBeforeResourceLoad( CefRefPtrGetURL().ToString().find("disable-devtool") != std::string::npos ) { + return RV_CANCEL; + } + if ( strScheme != "http" && strScheme != "https" && strScheme != "asset" ) { return RV_CANCEL; @@ -832,7 +1005,6 @@ bool ChromiumBrowser::OnFileDialog( CefRefPtr, const CefString&, const CefString&, const std::vector&, - int, CefRefPtr callback ) { callback->Cancel(); diff --git a/html_chromium/ChromiumBrowser.h b/html_chromium/ChromiumBrowser.h index 2be7372..79dfa80 100644 --- a/html_chromium/ChromiumBrowser.h +++ b/html_chromium/ChromiumBrowser.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "cef_start.h" #include "include/cef_client.h" @@ -27,6 +28,9 @@ class ChromiumBrowser ChromiumBrowser(); ~ChromiumBrowser(); + ChromiumBrowser(const ChromiumBrowser&) = delete; + ChromiumBrowser& operator=(const ChromiumBrowser&) = delete; + ImageData& GetImageData(); MessageQueue& GetMessageQueue(); void QueueMessage( MessageQueue::Message&& message ); @@ -54,7 +58,7 @@ class ChromiumBrowser public: // - // CefClient interface + // CefClient interface // CefRefPtr GetLifeSpanHandler() override { return this; } CefRefPtr GetLoadHandler() override { return this; } @@ -85,9 +89,9 @@ class ChromiumBrowser CefRefPtr&, bool* ) override; -// -// CefLoadHandler interface -// + // + // CefLoadHandler interface + // void OnLoadStart( CefRefPtr, CefRefPtr frame, CefLoadHandler::TransitionType ) override; void OnLoadEnd( CefRefPtr, CefRefPtr frame, int httpStatusCode ) override; void OnLoadError( CefRefPtr, CefRefPtr frame, CefLoadHandler::ErrorCode errorCode, const CefString& errorText, const CefString& failedURL ) override; @@ -98,15 +102,16 @@ class ChromiumBrowser void OnAddressChange( CefRefPtr, CefRefPtr frame, const CefString& url ) override; bool OnConsoleMessage( CefRefPtr, cef_log_severity_t level, const CefString& message, const CefString& source, int line ) override; void OnTitleChange( CefRefPtr, const CefString& title ) override; + bool OnCursorChange( CefRefPtr browser, CefCursorHandle, cef_cursor_type_t chromeCursor, const CefCursorInfo& ) override; // // CefRenderHandler interface // - void OnCursorChange( CefRefPtr, CefCursorHandle, CefRenderHandler::CursorType chromeCursor, const CefCursorInfo& ) override; void GetViewRect( CefRefPtr, CefRect& rect ) override; void OnPopupShow( CefRefPtr, bool show ) override; void OnPopupSize( CefRefPtr, const CefRect& rect ) override; void OnPaint( CefRefPtr, CefRenderHandler::PaintElementType type, const CefRenderHandler::RectList& dirtyRects, const void* buffer, int width, int height ) override; + void OnAcceleratedPaint( CefRefPtr browser, CefRenderHandler::PaintElementType type, const CefRenderHandler::RectList& dirtyRects, const CefAcceleratedPaintInfo& info) override; // // CefRequestHandler interface @@ -127,41 +132,40 @@ class ChromiumBrowser bool, bool ) override; -// -// CefResourceRequestHandler interface -// + // + // CefResourceRequestHandler interface + // ReturnValue OnBeforeResourceLoad( CefRefPtr, CefRefPtr, CefRefPtr request, - CefRefPtr ) override; + CefRefPtr ) override; void OnProtocolExecution( CefRefPtr, CefRefPtr, CefRefPtr, bool& allow_os_execution ) override; -// -// CefContextMenuHandler interface -// + // + // CefContextMenuHandler interface + // void OnBeforeContextMenu( CefRefPtr, CefRefPtr, CefRefPtr, CefRefPtr model ) override; -// -// CefDialogHandler interface -// + // + // CefDialogHandler interface + // bool OnFileDialog( CefRefPtr, FileDialogMode, const CefString&, const CefString&, const std::vector&, - int, CefRefPtr callback ) override; -// -// CefJSDialogHandler -// + // + // CefJSDialogHandler + // bool OnJSDialog( CefRefPtr, const CefString&, JSDialogType, @@ -196,6 +200,24 @@ class ChromiumBrowser bool m_OpenLinksExternally; + CefKeyEvent m_LastKeyEvent; + private: IMPLEMENT_REFCOUNTING( ChromiumBrowser ); -}; \ No newline at end of file + +private: + // Functions in this vector will be executed once our underlying CefBrowser is available + std::vector> m_Deferred; + + template + void RunOrDeferForInit(T func) + { + if (m_Browser != nullptr && m_BrowserHost != nullptr) + { + func(); + return; + } + + m_Deferred.push_back(std::move(func)); + } +}; diff --git a/html_chromium/ChromiumClient.cpp b/html_chromium/ChromiumClient.cpp index 53f665c..cdbbae3 100644 --- a/html_chromium/ChromiumClient.cpp +++ b/html_chromium/ChromiumClient.cpp @@ -4,10 +4,11 @@ #include "JSObjects.h" #include "cef_start.h" -#include "include/base/cef_bind.h" +#include "include/base/cef_callback.h" #include "include/wrapper/cef_closure_task.h" #include "cef_end.h" +#include #include ChromiumClient::ChromiumClient( CefRefPtr browser, IHtmlClientListener* listener ) @@ -70,7 +71,7 @@ void ChromiumClient::HandleJavaScriptCall( const char* objName, const char* func return; // TODO: This seems to be copying retArray - CefPostTask( TID_UI, base::Bind( &ChromiumBrowser::ExecuteCallback, m_Browser, callbackId, std::move( retArray ) ) ); + CefPostTask( TID_UI, base::BindOnce( &ChromiumBrowser::ExecuteCallback, m_Browser, callbackId, std::move( retArray ) ) ); } // @@ -78,85 +79,84 @@ void ChromiumClient::HandleJavaScriptCall( const char* objName, const char* func // void ChromiumClient::Close() { - CefPostTask( TID_UI, base::Bind( &ChromiumBrowser::Close, m_Browser ) ); + CefPostTask( TID_UI, base::BindOnce( &ChromiumBrowser::Close, m_Browser ) ); g_ChromiumSystem.OnClientClose( this ); - delete this; } void ChromiumClient::SetSize( int wide, int tall ) { - CefPostTask( TID_UI, base::Bind( &ChromiumBrowser::SetSize, m_Browser, wide, tall ) ); + CefPostTask( TID_UI, base::BindOnce( &ChromiumBrowser::SetSize, m_Browser, wide, tall ) ); } void ChromiumClient::SetFocused( bool hasFocus ) { - CefPostTask( TID_UI, base::Bind( &ChromiumBrowser::SetFocused, m_Browser, hasFocus ) ); + CefPostTask( TID_UI, base::BindOnce( &ChromiumBrowser::SetFocused, m_Browser, hasFocus ) ); } void ChromiumClient::SendKeyEvent( KeyEvent keyEvent ) { - CefPostTask( TID_UI, base::Bind( &ChromiumBrowser::SendKeyEvent, m_Browser, keyEvent ) ); + CefPostTask( TID_UI, base::BindOnce( &ChromiumBrowser::SendKeyEvent, m_Browser, keyEvent ) ); } void ChromiumClient::SendMouseMoveEvent( MouseEvent mouseEvent, bool mouseLeave ) { - CefPostTask( TID_UI, base::Bind( &ChromiumBrowser::SendMouseMoveEvent, m_Browser, mouseEvent, mouseLeave ) ); + CefPostTask( TID_UI, base::BindOnce( &ChromiumBrowser::SendMouseMoveEvent, m_Browser, mouseEvent, mouseLeave ) ); } void ChromiumClient::SendMouseWheelEvent( MouseEvent mouseEvent, int deltaX, int deltaY ) { - CefPostTask( TID_UI, base::Bind( &ChromiumBrowser::SendMouseWheelEvent, m_Browser, mouseEvent, deltaX, deltaY ) ); + CefPostTask( TID_UI, base::BindOnce( &ChromiumBrowser::SendMouseWheelEvent, m_Browser, mouseEvent, deltaX, deltaY ) ); } void ChromiumClient::SendMouseClickEvent( MouseEvent mouseEvent, MouseButton mouseButton, bool mouseUp, int clickCount ) { - CefPostTask( TID_UI, base::Bind( &ChromiumBrowser::SendMouseClickEvent, m_Browser, mouseEvent, mouseButton, mouseUp, clickCount ) ); + CefPostTask( TID_UI, base::BindOnce( &ChromiumBrowser::SendMouseClickEvent, m_Browser, mouseEvent, mouseButton, mouseUp, clickCount ) ); } void ChromiumClient::LoadUrl( const char* url ) { - CefPostTask( TID_UI, base::Bind( &ChromiumBrowser::LoadUrl, m_Browser, std::string( url ) ) ); + CefPostTask( TID_UI, base::BindOnce( &ChromiumBrowser::LoadUrl, m_Browser, std::string( url ) ) ); } void ChromiumClient::SetHtml( const char* html ) { - CefPostTask( TID_UI, base::Bind( &ChromiumBrowser::SetHtml, m_Browser, std::string( html ) ) ); + CefPostTask( TID_UI, base::BindOnce( &ChromiumBrowser::SetHtml, m_Browser, std::string( html ) ) ); } void ChromiumClient::Refresh() { - CefPostTask( TID_UI, base::Bind( &ChromiumBrowser::Refresh, m_Browser ) ); + CefPostTask( TID_UI, base::BindOnce( &ChromiumBrowser::Refresh, m_Browser ) ); } void ChromiumClient::Stop() { - CefPostTask( TID_UI, base::Bind( &ChromiumBrowser::Stop, m_Browser ) ); + CefPostTask( TID_UI, base::BindOnce( &ChromiumBrowser::Stop, m_Browser ) ); } void ChromiumClient::GoBack() { - CefPostTask( TID_UI, base::Bind( &ChromiumBrowser::GoBack, m_Browser ) ); + CefPostTask( TID_UI, base::BindOnce( &ChromiumBrowser::GoBack, m_Browser ) ); } void ChromiumClient::GoForward() { - CefPostTask( TID_UI, base::Bind( &ChromiumBrowser::GoForward, m_Browser ) ); + CefPostTask( TID_UI, base::BindOnce( &ChromiumBrowser::GoForward, m_Browser ) ); } void ChromiumClient::RunJavaScript( const char* code ) { - CefPostTask( TID_UI, base::Bind( &ChromiumBrowser::RunJavaScript, m_Browser, std::string( code ) ) ); + CefPostTask( TID_UI, base::BindOnce( &ChromiumBrowser::RunJavaScript, m_Browser, std::string( code ) ) ); } void ChromiumClient::RegisterJavaScriptFunction( const char* objName, const char* funcName ) { - CefPostTask( TID_UI, base::Bind( &ChromiumBrowser::RegisterJavaScriptFunction, m_Browser, std::string( objName ), std::string( funcName ) ) ); + CefPostTask( TID_UI, base::BindOnce( &ChromiumBrowser::RegisterJavaScriptFunction, m_Browser, std::string( objName ), std::string( funcName ) ) ); } void ChromiumClient::SetOpenLinksExternally( bool openLinksExternally ) { - CefPostTask( TID_UI, base::Bind( &ChromiumBrowser::SetOpenLinksExternally, m_Browser, openLinksExternally ) ); + CefPostTask( TID_UI, base::BindOnce( &ChromiumBrowser::SetOpenLinksExternally, m_Browser, openLinksExternally ) ); } bool ChromiumClient::LockImageData() @@ -186,4 +186,3 @@ const unsigned char* ChromiumClient::GetImageData( int& imageWide, int& imageTal ImageData& imageData = m_Browser->GetImageData(); return imageData.GetData( imageWide, imageTall ); } - diff --git a/html_chromium/ChromiumSystem.cpp b/html_chromium/ChromiumSystem.cpp index 4c497f1..e160cc7 100644 --- a/html_chromium/ChromiumSystem.cpp +++ b/html_chromium/ChromiumSystem.cpp @@ -1,4 +1,5 @@ -#include "ChromiumSystem.h" + +#include "ChromiumSystem.h" #include "ChromiumClient.h" #include "ChromiumBrowser.h" #include "ResourceHandler.h" @@ -8,18 +9,17 @@ #include "cef_start.h" #include "include/cef_app.h" #include "include/cef_origin_whitelist.h" -#ifdef OSX +#ifdef __APPLE__ #include "include/wrapper/cef_library_loader.h" #endif +#include "include/cef_version.h" #include "cef_end.h" +#include +#include #include - -#ifdef _WIN32 -#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING -#include -namespace fs = std::experimental::filesystem; -#endif +#include +namespace fs = std::filesystem; class ChromiumApp : public CefApp @@ -30,28 +30,40 @@ class ChromiumApp // void OnBeforeCommandLineProcessing( const CefString& process_type, CefRefPtr command_line ) override { - command_line->AppendSwitch( "disable-gpu" ); - command_line->AppendSwitch( "disable-gpu-compositing" ); + command_line->AppendSwitch( "enable-gpu" ); + command_line->AppendSwitch( "disable-gpu-compositing" ); // NOTE: Enabling GPU Compositing will make OnAcceleratedPaint run instead of OnPaint (CEF must be patched or NOTHING will run!) command_line->AppendSwitch( "disable-smooth-scrolling" ); #ifdef _WIN32 command_line->AppendSwitch( "enable-begin-frame-scheduling" ); #endif - command_line->AppendSwitch( "enable-system-flash" ); - // This can interfere with posix signals and break Breakpad -#ifdef POSIX +#ifdef __linux__ command_line->AppendSwitch( "disable-in-process-stack-traces" ); + + // Flatpak, AppImage, and Snap break sandboxing + // TODO(winter): It's not ideal to just outright turn off sandboxing...but Steam does it too, so + if (getenv("container") || getenv("APPIMAGE") || getenv("SNAP")) { + command_line->AppendSwitch("no-sandbox"); + } #endif -#ifdef OSX +#ifdef __APPLE__ command_line->AppendSwitch( "use-mock-keychain" ); #endif // https://bitbucket.org/chromiumembedded/cef/issues/2400 - command_line->AppendSwitchWithValue( "disable-features", "TouchpadAndWheelScrollLatching,AsyncWheelEvents" ); + // DXVAVideoDecoding must be disabled for Proton/Wine + // FirstPartySets causes crashing on Chromium 120, also it's an anti-privacy feature + command_line->AppendSwitchWithValue( "disable-features", "TouchpadAndWheelScrollLatching,AsyncWheelEvents,HardwareMediaKeyHandling,DXVAVideoDecoding,FirstPartySets" ); // Auto-play media command_line->AppendSwitchWithValue( "autoplay-policy", "no-user-gesture-required" ); + + // Disable site isolation until we implement passing registered Lua functions between processes + //command_line->AppendSwitch( "disable-site-isolation-trials" ); + + // Enable remote debugging; see also: settings.remote_debugging_port + //command_line->AppendSwitchWithValue( "remote-allow-origins", "http://localhost:9222" ); } void OnRegisterCustomSchemes( CefRawPtr registrar ) override @@ -67,12 +79,12 @@ class ChromiumApp typedef void* ( *CreateCefSandboxInfoFn )( ); typedef void ( *DestroyCefSandboxInfoFn )( void* ); -// Needs cleaning up. There's too much Windows shit. +// TODO: Needs cleaning up. There's too much Windows shit. bool ChromiumSystem::Init( const char* pBaseDir, IHtmlResourceHandler* pResourceHandler ) { g_pHtmlResourceHandler = pResourceHandler; -#ifdef OSX +#ifdef __APPLE__ static CefScopedLibraryLoader library_loader; if ( !library_loader.LoadInMain() ) { @@ -80,7 +92,7 @@ bool ChromiumSystem::Init( const char* pBaseDir, IHtmlResourceHandler* pResource } #endif -#ifdef POSIX +#if defined( __linux__ ) || defined( __APPLE__ ) // GMOD: GO - Chromium will replace Breakpad's signal handlers if we don't do this early int argc = 2; char arg1[] = "binary"; @@ -93,27 +105,32 @@ bool ChromiumSystem::Init( const char* pBaseDir, IHtmlResourceHandler* pResource CefSettings settings; std::string strBaseDir = pBaseDir; - + + //settings.remote_debugging_port = 9222; settings.remote_debugging_port = 0; settings.windowless_rendering_enabled = true; -#if defined( _WIN32 ) && !defined( NDEBUG ) - settings.no_sandbox = true; -#else + +#ifdef CEF_USE_SANDBOX settings.no_sandbox = false; +#else + settings.no_sandbox = true; #endif + settings.command_line_args_disabled = true; - settings.log_severity = LOGSEVERITY_VERBOSE; + settings.log_severity = LOGSEVERITY_DEFAULT; #ifdef _WIN32 + std::string platform = "Windows NT"; + // Chromium will be sad if we don't resolve any NTFS junctions for it // Is this really the only way Windows will let me do that? - auto hFile = CreateFile( strBaseDir.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); + auto hFile = CreateFileA( strBaseDir.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); bool useTempDir = false; if ( hFile != INVALID_HANDLE_VALUE ) { char pathBuf[MAX_PATH] = { 0 }; - if ( GetFinalPathNameByHandle( hFile, pathBuf, sizeof( pathBuf ), VOLUME_NAME_DOS ) ) + if ( GetFinalPathNameByHandleA( hFile, pathBuf, sizeof( pathBuf ), VOLUME_NAME_DOS ) ) { // If it's a network drive, we can't use it if ( strstr( pathBuf, "\\\\?\\UNC" ) == pathBuf ) @@ -156,15 +173,13 @@ bool ChromiumSystem::Init( const char* pBaseDir, IHtmlResourceHandler* pResource chromiumDir = targetPath.string(); } - CefString( &settings.user_agent ).FromString( "Mozilla/5.0 (Windows NT; Valve Source Client) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36 GMod/13" ); - // GMOD: GO - We use the same resources with 32-bit and 64-bit builds, so always use the 32-bit bin path for them CefString( &settings.resources_dir_path ).FromString( chromiumDir ); CefString( &settings.locales_dir_path ).FromString( chromiumDir + "/locales" ); settings.multi_threaded_message_loop = true; -#elif LINUX - CefString( &settings.user_agent ).FromString( "Mozilla/5.0 (Linux; Valve Source Client) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36 GMod/13" ); +#elif __linux__ + std::string platform = "Linux"; #if defined(__x86_64__) || defined(_WIN64) CefString( &settings.browser_subprocess_path ).FromString( strBaseDir + "/bin/linux64/chromium_process" ); @@ -177,16 +192,68 @@ bool ChromiumSystem::Init( const char* pBaseDir, IHtmlResourceHandler* pResource CefString( &settings.locales_dir_path ).FromString( strBaseDir + "/bin/linux32/chromium/locales" ); settings.multi_threaded_message_loop = true; -#elif OSX - CefString( &settings.user_agent ).FromString( "Mozilla/5.0 (Macintosh; Intel Mac OS X; Valve Source Client) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36 GMod/13" ); +#elif __APPLE__ + std::string platform = "Macintosh; Intel Mac OS X"; #else #error #endif - CefString( &settings.log_file ).FromString( strBaseDir + "/chromium.log" ); + std::string chrome_version = std::to_string(CHROME_VERSION_MAJOR) + ".0.0.0"; + CefString(&settings.user_agent).FromString("Mozilla/5.0 (" + platform + "; Valve Source Client) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" + chrome_version + " Safari/537.36 GMod/13"); + + // Rotate log file + // TODO(winter): This is probably not the best place to be doing this + std::error_code rotateError; + std::string curLogPath = strBaseDir + "/chromium.log"; + std::string lastLogPath = strBaseDir + "/chromium.log.1"; + + std::string cefCachePath = strBaseDir + "/ChromiumCache"; + std::string cefLockFilePath = cefCachePath + "/lockfile"; + + // TODO(winter): What if GMod/CEF crashes? Will the lockfile still be there? + if (fs::exists(cefLockFilePath)) { + pResourceHandler->Message("Skipping Chromium log rotation (lockfile exists)...\n"); + + // TODO(winter): See also ChromiumSystem::Shutdown; we should be clearing these multirun caches instead of keeping them around/reusing them (they can be >500MB EACH) + unsigned int multirunInstanceID = 0; + //while (fs::exists(cefCachePath)) { + while (fs::exists(cefCachePath) && fs::exists(cefLockFilePath)) { + multirunInstanceID++; + cefCachePath = strBaseDir + "/ChromiumCacheMultirun/" + std::to_string(multirunInstanceID); + cefLockFilePath = cefCachePath + "/lockfile"; + } - // Grab our Sandbox info from the game exe -#if defined( _WIN32 ) && defined( NDEBUG ) + m_MultirunCacheDir = cefCachePath; + + std::string tmpCacheMsg = "Using temporary Chromium cache to support multirun: " + m_MultirunCacheDir + "\n"; + pResourceHandler->Message(tmpCacheMsg.c_str()); + } else { + fs::copy_file(curLogPath, lastLogPath, fs::copy_options::overwrite_existing, rotateError); + + if (rotateError) { + const std::string rotateErrorMsg = "Couldn't rotate chromium.log (copy): " + rotateError.message() + "\n"; + pResourceHandler->Message(rotateErrorMsg.c_str()); + rotateError.clear(); + } + + // TODO(winter): Truncate instead? + fs::remove(curLogPath, rotateError); + + if (rotateError) { + const std::string rotateErrorMsg = "Couldn't rotate chromium.log (remove): " + rotateError.message() + "\n"; + pResourceHandler->Message(rotateErrorMsg.c_str()); + rotateError.clear(); + } + } + + CefString( &settings.log_file ).FromString( curLogPath ); + + // CEF 120+ requires this otherwise CEF applications will trample each other + CefString( &settings.root_cache_path ).FromString( cefCachePath ); + CefString( &settings.cache_path ).FromString( cefCachePath ); + + // Grab our Sandbox info from the "game" exe +#if defined(_WIN32) && defined(CEF_USE_SANDBOX) HMODULE pModule; if ( !GetModuleHandleEx( GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, nullptr, &pModule ) ) @@ -214,10 +281,11 @@ bool ChromiumSystem::Init( const char* pBaseDir, IHtmlResourceHandler* pResource if ( !CefInitialize( main_args, settings, new ChromiumApp, sandbox_info ) ) { + pResourceHandler->Message("CefInitialize failed!\n"); return false; } -#if defined( _WIN32 ) && defined( NDEBUG ) +#if defined(_WIN32) && defined(CEF_USE_SANDBOX) DestroyCefSandboxInfo( sandbox_info ); #endif @@ -245,7 +313,7 @@ bool ChromiumSystem::Init( const char* pBaseDir, IHtmlResourceHandler* pResource CefAddCrossOriginWhitelistEntry( "asset://html", "asset", "", true ); } -#ifdef OSX +#ifdef __APPLE__ CefDoMessageLoopWork(); #endif @@ -255,23 +323,42 @@ bool ChromiumSystem::Init( const char* pBaseDir, IHtmlResourceHandler* pResource void ChromiumSystem::Shutdown() { CefShutdown(); + + // Delete temporary ChromiumCacheMultirun if it exists + // TODO(winter): For some reason CEF still hasn't released the handles it has for these files even though CefShutdown has finished and the lockfile is gone... + /* + if (!m_MultirunCacheDir.empty()) { + while (fs::exists(m_MultirunCacheDir + "/lockfile")) { + // Spin until the lockfile is released by CEF + } + + std::error_code removeTempError; + fs::remove_all(m_MultirunCacheDir, removeTempError); + + if (removeTempError) { + const std::string removeTempErrorMsg = "Couldn't remove temporary Chromium cache: " + removeTempError.message() + "\n"; + g_pHtmlResourceHandler->Message(removeTempErrorMsg.c_str()); + } + } + */ } IHtmlClient* ChromiumSystem::CreateClient( IHtmlClientListener* listener ) { CefWindowInfo windowInfo; windowInfo.SetAsWindowless( 0 ); +#ifdef _WIN32 + //windowInfo.shared_texture_enabled = true; +#endif CefBrowserSettings browserSettings; CefString( &browserSettings.default_encoding ).FromString( "UTF-8" ); browserSettings.windowless_frame_rate = 60; browserSettings.javascript_access_clipboard = STATE_DISABLED; browserSettings.javascript_close_windows = STATE_DISABLED; - browserSettings.universal_access_from_file_urls = STATE_DISABLED; - browserSettings.file_access_from_file_urls = STATE_DISABLED; - browserSettings.webgl = STATE_DISABLED; + browserSettings.webgl = STATE_ENABLED; - CefRefPtr cefClient( new ChromiumBrowser ); + CefRefPtr cefClient( new ChromiumBrowser() ); // Queue the browser creation. It's async, but ChromiumBrowser will handle it all. CefBrowserHost::CreateBrowser( windowInfo, cefClient, "", browserSettings, nullptr, nullptr ); @@ -294,7 +381,7 @@ void ChromiumSystem::Update() m_RequestsLock.Release(); // macOS will want me -#ifdef OSX +#ifdef __APPLE__ CefDoMessageLoopWork(); #endif @@ -354,4 +441,4 @@ JSValue ChromiumSystem::CreateHashMap( const char** pKeys, const size_t* pKeySiz } ChromiumSystem g_ChromiumSystem; -HTMLSYSTEM_EXPORT( g_ChromiumSystem ); \ No newline at end of file +HTMLSYSTEM_EXPORT( g_ChromiumSystem ); diff --git a/html_chromium/ChromiumSystem.h b/html_chromium/ChromiumSystem.h index 87fd0c6..b75e684 100644 --- a/html_chromium/ChromiumSystem.h +++ b/html_chromium/ChromiumSystem.h @@ -36,6 +36,8 @@ class ChromiumSystem : public IHtmlSystem base::Lock m_RequestsLock; std::vector> m_Requests; + + std::string m_MultirunCacheDir; }; // We've got a few bits of code that need to access this directly diff --git a/html_chromium/DomCodeConverter.h b/html_chromium/DomCodeConverter.h new file mode 100644 index 0000000..01aa98f --- /dev/null +++ b/html_chromium/DomCodeConverter.h @@ -0,0 +1,178 @@ +#ifndef DOMCODECONVERTER_H_ +#define DOMCODECONVERTER_H_ + +#include + +#include "chromium/dom_code.h" + +using namespace std; +using namespace ui; + +// WARN(winter): This might not be right all the time! Tested on Windows 10 with US Keyboard Layout with English (United States) locale and UTF-8 enabled +// TODO(winter): Incomplete! Populate with https://github.com/chromium/chromium/blob/master/ui/events/keycodes/keyboard_defines_win.h +// TODO(winter): Make LinuxToDomMap with https://github.com/chromium/chromium/blob/master/ui/events/keycodes/keyboard_codes_posix.h +map WindowsToDomMap = { + // Control (1) + {0x08, DomCode::BACKSPACE}, + {0x09, DomCode::TAB}, + {0x0D, DomCode::ENTER}, + {0x10, DomCode::SHIFT_LEFT}, + //{0x10, DomCode::SHIFT_RIGHT}, // TODO: Duplicate! + {0x11, DomCode::CONTROL_LEFT}, + //{0x11, DomCode::CONTROL_RIGHT}, // TODO: Duplicate! + {0x12, DomCode::ALT_LEFT}, + //{0x12, DomCode::ALT_RIGHT}, // TODO: Duplicate! + {0x13, DomCode::PAUSE}, + {0x14, DomCode::CAPS_LOCK}, + {0x1B, DomCode::ESCAPE}, + {0x20, DomCode::SPACE}, + + // Control (2) + {0x21, DomCode::PAGE_UP}, + {0x22, DomCode::PAGE_DOWN}, + {0x23, DomCode::END}, + {0x24, DomCode::HOME}, + {0x25, DomCode::ARROW_LEFT}, + {0x26, DomCode::ARROW_UP}, + {0x27, DomCode::ARROW_RIGHT}, + {0x28, DomCode::ARROW_DOWN}, + {0x29, DomCode::SELECT}, + {0x2A, DomCode::PRINT}, + {0x2C, DomCode::PRINT_SCREEN}, + {0x2D, DomCode::INSERT}, + {0x2E, DomCode::DEL}, + + // Digits + {0x30, DomCode::DIGIT0}, + {0x31, DomCode::DIGIT1}, + {0x32, DomCode::DIGIT2}, + {0x33, DomCode::DIGIT3}, + {0x34, DomCode::DIGIT4}, + {0x35, DomCode::DIGIT5}, + {0x36, DomCode::DIGIT6}, + {0x37, DomCode::DIGIT7}, + {0x38, DomCode::DIGIT8}, + {0x39, DomCode::DIGIT9}, + + // Symbols (2) + {0x3A, DomCode::SEMICOLON}, // : + {0x3B, DomCode::SEMICOLON}, // ; + {0x3C, DomCode::COMMA}, // < + {0x3D, DomCode::EQUAL}, // = + {0x3E, DomCode::PERIOD}, // > + {0x3F, DomCode::SLASH}, // ? + {0x40, DomCode::DIGIT2}, // @ + + // Uppercase + {0x41, DomCode::US_A}, + {0x42, DomCode::US_B}, + {0x43, DomCode::US_C}, + {0x44, DomCode::US_D}, + {0x45, DomCode::US_E}, + {0x46, DomCode::US_F}, + {0x47, DomCode::US_G}, + {0x48, DomCode::US_H}, + {0x49, DomCode::US_I}, + {0x4A, DomCode::US_J}, + {0x4B, DomCode::US_K}, + {0x4C, DomCode::US_L}, + {0x4D, DomCode::US_M}, + {0x4E, DomCode::US_N}, + {0x4F, DomCode::US_O}, + {0x50, DomCode::US_P}, + {0x51, DomCode::US_Q}, + {0x52, DomCode::US_R}, + {0x53, DomCode::US_S}, + {0x54, DomCode::US_T}, + {0x55, DomCode::US_U}, + {0x56, DomCode::US_V}, + {0x57, DomCode::US_W}, + {0x58, DomCode::US_X}, + {0x59, DomCode::US_Y}, + {0x5A, DomCode::US_Z}, + + // Control (3) + // TODO: Conflict! + //{0x5B, DomCode::META_LEFT}, + //{0x5C, DomCode::META_RIGHT}, + + // Symbols (3) + {0x5B, DomCode::BRACKET_LEFT}, // [ + {0x5C, DomCode::BACKSLASH}, // \ (backslash) + {0x5D, DomCode::BRACKET_RIGHT}, // ] + {0x5E, DomCode::DIGIT6}, // ^ + {0x5F, DomCode::MINUS}, // _ + {0x60, DomCode::BACKQUOTE}, // ` + + // Lowercase + // TODO: This makes sense according to UTF, but not Windows' VK_* defines... + {0x61, DomCode::US_A}, + {0x62, DomCode::US_B}, + {0x63, DomCode::US_C}, + {0x64, DomCode::US_D}, + {0x65, DomCode::US_E}, + {0x66, DomCode::US_F}, + {0x67, DomCode::US_G}, + {0x68, DomCode::US_H}, + {0x69, DomCode::US_I}, + {0x6A, DomCode::US_J}, + {0x6B, DomCode::US_K}, + {0x6C, DomCode::US_L}, + {0x6D, DomCode::US_M}, + {0x6E, DomCode::US_N}, + {0x6F, DomCode::US_O}, + {0x70, DomCode::US_P}, + {0x71, DomCode::US_Q}, + {0x72, DomCode::US_R}, + {0x73, DomCode::US_S}, + {0x74, DomCode::US_T}, + {0x75, DomCode::US_U}, + {0x76, DomCode::US_V}, + {0x77, DomCode::US_W}, + {0x78, DomCode::US_X}, + {0x79, DomCode::US_Y}, + {0x7A, DomCode::US_Z}, + + // Symbols (4) + {0x7B, DomCode::BRACKET_LEFT}, // { + {0x7C, DomCode::BACKSLASH}, // | + {0x7D, DomCode::BRACKET_RIGHT}, // } + {0x7E, DomCode::BACKQUOTE}, // ~ +}; + +// Conflicts with the normal map...but symbols seem to be broken in a way we can detect! +map NullKeyDownWindowsToDomMap = { + // Symbols (1) + {0x21, DomCode::DIGIT1}, // ! + {0x22, DomCode::QUOTE}, // " + {0x23, DomCode::DIGIT3}, // # + {0x24, DomCode::DIGIT4}, // $ + {0x25, DomCode::DIGIT5}, // % + {0x26, DomCode::DIGIT7}, // & + {0x27, DomCode::QUOTE}, // ' + {0x28, DomCode::DIGIT9}, // ( + {0x29, DomCode::DIGIT0}, // ) + {0x2A, DomCode::DIGIT8}, // * + {0x2B, DomCode::EQUAL}, // + + {0x2C, DomCode::COMMA}, // , + {0x2D, DomCode::MINUS}, // - + {0x2E, DomCode::PERIOD}, // . + {0x2F, DomCode::SLASH}, // / +}; + +// static? +DomCode WindowsKeyCodeToDomCode(int windows_key_code, bool lastkeydown_null) { + if (lastkeydown_null) { + if (windows_key_code > 0 && NullKeyDownWindowsToDomMap.count(windows_key_code) == 1) { + return NullKeyDownWindowsToDomMap.at(windows_key_code); + } + } else { + if (windows_key_code > 0 && WindowsToDomMap.count(windows_key_code) == 1) { + return WindowsToDomMap.at(windows_key_code); + } + } + + return DomCode::NONE; +} + +#endif // DOMCODECONVERTER_H_ diff --git a/html_chromium/HtmlResourceHandler.cpp b/html_chromium/HtmlResourceHandler.cpp index 0914ec7..4e3c922 100644 --- a/html_chromium/HtmlResourceHandler.cpp +++ b/html_chromium/HtmlResourceHandler.cpp @@ -33,7 +33,7 @@ bool HtmlResourceHandler::ProcessRequest( CefRefPtr request, CefRefP return true; } -void HtmlResourceHandler::GetResponseHeaders( CefRefPtr response, cef_int64& responseLength, CefString& redirectUrl ) +void HtmlResourceHandler::GetResponseHeaders( CefRefPtr response, int64_t& responseLength, CefString& redirectUrl ) { CefResponse::HeaderMap headerMap; headerMap.emplace( "X-XSS-Protection", "0" ); @@ -49,7 +49,7 @@ bool HtmlResourceHandler::ReadResponse( void* data_out, int bytes_to_read, int& if ( m_Html.size() <= 0 ) return false; - bytes_to_read = std::min( bytes_to_read, m_Html.size() - m_Current ); + bytes_to_read = std::min( bytes_to_read, m_Html.size() - m_Current ); memcpy( data_out, m_Html.data() + m_Current, bytes_to_read ); bytes_read = bytes_to_read; @@ -61,4 +61,4 @@ bool HtmlResourceHandler::ReadResponse( void* data_out, int bytes_to_read, int& void HtmlResourceHandler::Cancel() { // ... -} \ No newline at end of file +} diff --git a/html_chromium/HtmlResourceHandler.h b/html_chromium/HtmlResourceHandler.h index 70b776c..6788674 100644 --- a/html_chromium/HtmlResourceHandler.h +++ b/html_chromium/HtmlResourceHandler.h @@ -28,7 +28,7 @@ class HtmlResourceHandler // CefResourceHandler interface // bool ProcessRequest( CefRefPtr request, CefRefPtr callback ) override; - void GetResponseHeaders( CefRefPtr response, cef_int64& responseLength, CefString& redirectUrl ) override; + void GetResponseHeaders( CefRefPtr response, int64_t& responseLength, CefString& redirectUrl ) override; bool ReadResponse( void* data_out, int bytes_to_read, int& bytes_read, CefRefPtr callback ) override; void Cancel() override; diff --git a/html_chromium/ImageData.h b/html_chromium/ImageData.h index e052aeb..47deef7 100644 --- a/html_chromium/ImageData.h +++ b/html_chromium/ImageData.h @@ -12,6 +12,7 @@ class ImageData : m_Dirty( false ) , m_Wide( 0 ) , m_Tall( 0 ) + , m_Size( 0 ) , m_Data( nullptr ) {} @@ -30,21 +31,63 @@ class ImageData m_Lock.Release(); } - void SetData( const unsigned char* data, int wide, int tall ) + bool ResizeData(int wide, int tall) { - if ( m_Wide != wide || m_Tall != tall ) + bool resized = wide * tall * 4 > m_Size; + if ( resized ) { - delete[] m_Data; - m_Data = new unsigned char[wide * tall * 4]; + unsigned char* old_Data = m_Data; + m_Size = wide * tall * 4; + m_Data = new unsigned char[m_Size]; + delete[] old_Data; } - memcpy( m_Data, data, wide * tall * 4 ); m_Wide = wide; m_Tall = tall; + return resized; + } + + void Blit(const unsigned char* data, int x, int y, int width, int height) + { + if (x + width > m_Wide || y + height > m_Tall) { + // TODO: Add error log here for invalid Blit. + return; + } + + for ( int SrcY = 0; SrcY < height; SrcY++ ) + { + // TODO: See if AVX copy or SIMD can speed this up? + memcpy( &m_Data[( SrcY + y ) * m_Wide * 4 + x * 4], &data[( SrcY + y ) * m_Wide * 4 + x * 4], width * 4 ); + } + m_Dirty = true; + } + + void BlitRelative(const unsigned char* data, int x, int y, int width, int height) + { + if (x + width > m_Wide || y + height > m_Tall) { + // TODO: Add error log here for invalid Blit. + return; + } + + for ( int SrcY = 0; SrcY < height; SrcY++ ) + { + memcpy( &m_Data[( SrcY + y ) * m_Wide * 4 + x * 4], &data[SrcY * width * 4], width * 4 ); + } + m_Dirty = true; + } + + void SetData( const unsigned char* data, int wide, int tall ) + { + if (wide > m_Wide || tall > m_Tall) { + // TODO: Add warning log here for invalid data set. Call ResizeData first. + ResizeData(wide, tall); + } + + memcpy( m_Data, data, wide * tall * 4 ); m_Dirty = true; } - unsigned char* GetData( int& wide, int& tall ) + const unsigned char* GetData( int& wide, int& tall ) { wide = m_Wide; tall = m_Tall; @@ -62,12 +105,12 @@ class ImageData } private: - friend class ChromiumBrowser; bool m_Dirty; int m_Wide; int m_Tall; + int m_Size; base::Lock m_Lock; unsigned char* m_Data; -}; \ No newline at end of file +}; diff --git a/html_chromium/JSObjects.cpp b/html_chromium/JSObjects.cpp index c9525a0..8ed6061 100644 --- a/html_chromium/JSObjects.cpp +++ b/html_chromium/JSObjects.cpp @@ -92,4 +92,4 @@ JSValue::IHashMap* JSHashMap::Copy() const const std::unordered_map& JSHashMap::GetInternalData() const { return _data; -} \ No newline at end of file +} diff --git a/html_chromium/JSObjects.h b/html_chromium/JSObjects.h index 1bc4420..3e997f1 100644 --- a/html_chromium/JSObjects.h +++ b/html_chromium/JSObjects.h @@ -82,4 +82,4 @@ class JSHashMap : public JSValue::IHashMap private: std::unordered_map _data; std::unordered_map::const_iterator _iterator; -}; \ No newline at end of file +}; diff --git a/html_chromium/MessageQueue.h b/html_chromium/MessageQueue.h index 67deba6..1362341 100644 --- a/html_chromium/MessageQueue.h +++ b/html_chromium/MessageQueue.h @@ -69,4 +69,4 @@ class MessageQueue private: base::Lock m_Lock; std::queue m_Messages; -}; \ No newline at end of file +}; diff --git a/html_chromium/ResourceHandler.cpp b/html_chromium/ResourceHandler.cpp index 7c01d5e..040ca2f 100644 --- a/html_chromium/ResourceHandler.cpp +++ b/html_chromium/ResourceHandler.cpp @@ -56,6 +56,13 @@ bool ResourceHandler::ProcessRequest( CefRefPtr request, CefRefPtr response, cef_int64& responseLength, CefString& redirectUrl ) +void ResourceHandler::GetResponseHeaders( CefRefPtr response, int64_t& responseLength, CefString& redirectUrl ) { if ( m_Buffer == nullptr ) { @@ -98,7 +105,7 @@ void ResourceHandler::GetResponseHeaders( CefRefPtr response, cef_i bool ResourceHandler::ReadResponse( void* data_out, int bytes_to_read, int& bytes_read, CefRefPtr callback ) { - bytes_to_read = std::min( bytes_to_read, m_BufferLength - ( m_BufferPtr - m_Buffer ) ); + bytes_to_read = std::min( bytes_to_read, m_BufferLength - ( m_BufferPtr - m_Buffer ) ); memcpy( data_out, m_BufferPtr, bytes_to_read ); bytes_read = bytes_to_read; diff --git a/html_chromium/ResourceHandler.h b/html_chromium/ResourceHandler.h index 4bc1981..b6fb726 100644 --- a/html_chromium/ResourceHandler.h +++ b/html_chromium/ResourceHandler.h @@ -34,7 +34,7 @@ class ResourceHandler // CefResourceHandler interface // bool ProcessRequest( CefRefPtr request, CefRefPtr callback ) override; - void GetResponseHeaders( CefRefPtr response, cef_int64& responseLength, CefString& redirectUrl ) override; + void GetResponseHeaders( CefRefPtr response, int64_t& responseLength, CefString& redirectUrl ) override; bool ReadResponse( void* data_out, int bytes_to_read, int& bytes_read, CefRefPtr callback ) override; void Cancel() override; diff --git a/html_chromium/cef_end.h b/html_chromium/cef_end.h index b495497..85b735f 100644 --- a/html_chromium/cef_end.h +++ b/html_chromium/cef_end.h @@ -3,4 +3,4 @@ // #undef int64 -#undef uint64 \ No newline at end of file +#undef uint64 diff --git a/html_chromium/cef_start.h b/html_chromium/cef_start.h index 5a0aa3b..fef98c0 100644 --- a/html_chromium/cef_start.h +++ b/html_chromium/cef_start.h @@ -3,4 +3,4 @@ // #define int64 cef_int64 -#define uint64 cef_uint64 \ No newline at end of file +#define uint64 cef_uint64 diff --git a/html_chromium/chromium/dom_code.h b/html_chromium/chromium/dom_code.h new file mode 100644 index 0000000..5b95d2e --- /dev/null +++ b/html_chromium/chromium/dom_code.h @@ -0,0 +1,24 @@ +// Copied from https://github.com/chromium/chromium/blob/main/ui/events/keycodes/dom/dom_code.h +// Last Updated - August 7, 2024: https://github.com/chromium/chromium/commit/c0a18161acdf82d65faf66f964154cc2aeaee6fe + +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_EVENTS_KEYCODES_DOM_DOM_CODE_H_ +#define UI_EVENTS_KEYCODES_DOM_DOM_CODE_H_ + +#include + +namespace ui { + +// Declares named values for each of the recognized DOM Code values. +#define DOM_CODE(usb, evdev, xkb, win, mac, code, id) id = usb +#define DOM_CODE_DECLARATION enum class DomCode : uint32_t +#include "dom_code_data.inc" +#undef DOM_CODE +#undef DOM_CODE_DECLARATION + +} // namespace ui + +#endif // UI_EVENTS_KEYCODES_DOM_DOM_CODE_H_ diff --git a/html_chromium/chromium/dom_code_data.inc b/html_chromium/chromium/dom_code_data.inc new file mode 100644 index 0000000..bdf54ae --- /dev/null +++ b/html_chromium/chromium/dom_code_data.inc @@ -0,0 +1,604 @@ +// Copied from https://github.com/chromium/chromium/blob/master/ui/events/keycodes/dom/dom_code_data.inc +// Last Updated - August 7, 2024: https://github.com/chromium/chromium/commit/35bc8ada48cd36cd115d6146c48b25a0a6fbebdc + +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file has no header guard because it is explicitly intended +// to be included more than once with different definitions of the +// macros DOM_CODE and DOM_CODE_DECLARATION. + +// Data in this file was created by referencing: +// [0] USB HID Usage Tables, +// http://www.usb.org/developers/hidpage/Hut1_12v2.pdf +// [1] DOM Level 3 KeyboardEvent code Values, +// http://www.w3.org/TR/uievents-code/ +// [2] OS X +// [3] Linux and hid-input.c +// [4] USB HID to PS/2 Scan Code Translation Table +// http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf +// [5] Keyboard Scan Code Specification +// http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/scancode.doc + +// General notes: +// +// This table provides the definition of ui::DomCode (UI Events |code|) values +// as well as mapping between scan codes and DomCode. Some entries have no +// defined scan codes; these are present only to allow those UI Events |code| +// strings to be represented by DomCode. A few have a null code; these define +// mappings with a DomCode:: value but no |code| string, typically because +// they end up used in shortcuts but not standardized in UI Events; e.g. +// DomCode::BRIGHTNESS_UP. Commented-out entries document USB codes that are +// potentially interesting but not currently used. + +// Linux notes: +// +// All USB codes that are listed here and that are supported by the kernel +// (as of 4.2) have their evdev/xkb scan codes recorded; if an evdev/xkb +// code is 0, it is because the kernel USB driver does not handle that key. +// +// Some Linux kernel mappings for USB keys may seem counterintuitive: +// +// [L1] Although evdev 0x163 KEY_CLEAR exists, Linux does not use it +// for any USB keys. Linux maps USB 0x07009c [Keyboard Clear] and +// 0x0700d8 [Keypad Clear] to KEY_DELETE "Delete", so those codes are +// not distinguishable by applications, and UI Events "NumpadClear" +// is therefore not supported. USB 0x0700A2 [Keyboard Clear/Again] +// is not mapped by the kernel at all. +// +// [L2] 'Menu' and 'Props' naming differs between evdev and USB / UI Events. +// USB 0x010085 [System Main Menu] and USB 0x0C0040 [Menu Mode] both +// map to evdev 0x8B KEY_MENU (which has no corresponding UI Events +// |code|). USB 0x070076 [Keyboard Menu] does not map to KEY_MENU; +// it maps to evdev 0x82 KEY_PROPS, which is not the same as USB and +// UI Events "Props". USB 0x0700A3 [Props], which does correspond to +// UI Events "Props", is not mapped by the kernel. (And all of these +// are distinct from UI Events' "ContextMenu", which corresponds to +// USB 0x070065 [Keyboard Application] via evdev 0x7F KEY_COMPOSE, +// following Windows convention.) + +// Windows notes: +// +// The set of scan codes supported here may not be complete. +// +// [W1] Windows maps both USB 0x070094 [Lang5] and USB 0x070073 [F24] to the +// same scan code, 0x76. (Microsoft's defined scan codes for F13 - F24 +// appear to be the result of accidentally mapping an IBM Set 3 terminal +// keyboard, rather than an IBM Set 2 PC keyboard, through the BIOS +// 2-to-1 table.) We map 0x76 to F24 here, since Lang5 appears unused +// in practice (its declared function, Zenkaku/Hankaku switch, is +// conventionally placed on Backquote by Japanese keyboards). + +// Macintosh notes: +// +// The set of scan codes supported here may not be complete. +// +// [M1] OS X maps USB 0x070049 [Insert] as well as USB 0x070075 [Help] to +// scan code 0x72 kVK_Help. We map this to UI Events 'Insert', since +// Apple keyboards with USB 0x070049 [Insert] labelled "Help" have not +// been made since 2007. + +DOM_CODE_DECLARATION { + + // USB evdev XKB Win Mac Code + DOM_CODE(0x000000, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NONE), // Invalid + + // ========================================= + // Non-USB codes + // ========================================= + + // USB evdev XKB Win Mac Code + DOM_CODE(0x000010, 0x0000, 0x0000, 0x0000, 0xffff, "Hyper", HYPER), + DOM_CODE(0x000011, 0x0000, 0x0000, 0x0000, 0xffff, "Super", SUPER), + DOM_CODE(0x000012, 0x0000, 0x0000, 0x0000, 0xffff, "Fn", FN), + DOM_CODE(0x000013, 0x0000, 0x0000, 0x0000, 0xffff, "FnLock", FN_LOCK), + DOM_CODE(0x000014, 0x0000, 0x0000, 0x0000, 0xffff, "Suspend", SUSPEND), + DOM_CODE(0x000015, 0x0000, 0x0000, 0x0000, 0xffff, "Resume", RESUME), + DOM_CODE(0x000016, 0x0000, 0x0000, 0x0000, 0xffff, "Turbo", TURBO), + // TODO(b/146683484): HID usage for privacy screen control is not yet + // approved. Once it is update the code here. + // TODO(https://crbug.com/952051): Privacy screen will not have a DOM |code| + // name defined as it is not exposed to web content. + DOM_CODE(0x000017, 0x0279, 0x0281, 0x0000, 0xffff, "PrivacyScreenToggle", + PRIVACY_SCREEN_TOGGLE), // Privacy Screen Toggle + // As with privacy screen, microphone mute toggle will not be exposed to web + // content. + DOM_CODE(0x000018, 0x00f8, 0x0100, 0x0000, 0xffff, "MicrophoneMuteToggle", + MICROPHONE_MUTE_TOGGLE), // Microphone Mute Toggle + + // ========================================= + // USB Usage Page 0x01: Generic Desktop Page + // ========================================= + + // Sleep could be encoded as USB#0c0032, but there's no corresponding WakeUp + // in the 0x0c USB page. + // USB evdev XKB Win Mac + DOM_CODE(0x010082, 0x008e, 0x0096, 0xe05f, 0xffff, "Sleep", SLEEP), // SystemSleep + DOM_CODE(0x010083, 0x008f, 0x0097, 0xe063, 0xffff, "WakeUp", WAKE_UP), + DOM_CODE(0x0100b5, 0x00e3, 0x00eb, 0x0000, 0xffff, "DisplayToggleIntExt", + DISPLAY_TOGGLE_INT_EXT), // System Display Toggle Int/Ext + + + // ========================================= + // USB Usage Page 0x07: Keyboard/Keypad Page + // ========================================= + + // TODO(garykac): + // XKB#005c ISO Level3 Shift (AltGr) + // XKB#005e <>|| + // XKB#006d Linefeed + // XKB#008a SunProps cf. USB#0700a3 CrSel/Props + // XKB#008e SunOpen + // Mac#003f kVK_Function + // Mac#000a kVK_ISO_Section (ISO keyboards only) + // Mac#0066 kVK_JIS_Eisu (USB#07008a Henkan?) + + // USB evdev XKB Win Mac + DOM_CODE(0x070000, 0x0000, 0x0000, 0x0000, 0xffff, NULL, USB_RESERVED), + DOM_CODE(0x070001, 0x0000, 0x0000, 0x00ff, 0xffff, NULL, USB_ERROR_ROLL_OVER), + DOM_CODE(0x070002, 0x0000, 0x0000, 0x00fc, 0xffff, NULL, USB_POST_FAIL), + DOM_CODE(0x070003, 0x0000, 0x0000, 0x0000, 0xffff, NULL, USB_ERROR_UNDEFINED), + DOM_CODE(0x070004, 0x001e, 0x0026, 0x001e, 0x0000, "KeyA", US_A), // aA + DOM_CODE(0x070005, 0x0030, 0x0038, 0x0030, 0x000b, "KeyB", US_B), // bB + DOM_CODE(0x070006, 0x002e, 0x0036, 0x002e, 0x0008, "KeyC", US_C), // cC + DOM_CODE(0x070007, 0x0020, 0x0028, 0x0020, 0x0002, "KeyD", US_D), // dD + + DOM_CODE(0x070008, 0x0012, 0x001a, 0x0012, 0x000e, "KeyE", US_E), // eE + DOM_CODE(0x070009, 0x0021, 0x0029, 0x0021, 0x0003, "KeyF", US_F), // fF + DOM_CODE(0x07000a, 0x0022, 0x002a, 0x0022, 0x0005, "KeyG", US_G), // gG + DOM_CODE(0x07000b, 0x0023, 0x002b, 0x0023, 0x0004, "KeyH", US_H), // hH + DOM_CODE(0x07000c, 0x0017, 0x001f, 0x0017, 0x0022, "KeyI", US_I), // iI + DOM_CODE(0x07000d, 0x0024, 0x002c, 0x0024, 0x0026, "KeyJ", US_J), // jJ + DOM_CODE(0x07000e, 0x0025, 0x002d, 0x0025, 0x0028, "KeyK", US_K), // kK + DOM_CODE(0x07000f, 0x0026, 0x002e, 0x0026, 0x0025, "KeyL", US_L), // lL + + DOM_CODE(0x070010, 0x0032, 0x003a, 0x0032, 0x002e, "KeyM", US_M), // mM + DOM_CODE(0x070011, 0x0031, 0x0039, 0x0031, 0x002d, "KeyN", US_N), // nN + DOM_CODE(0x070012, 0x0018, 0x0020, 0x0018, 0x001f, "KeyO", US_O), // oO + DOM_CODE(0x070013, 0x0019, 0x0021, 0x0019, 0x0023, "KeyP", US_P), // pP + DOM_CODE(0x070014, 0x0010, 0x0018, 0x0010, 0x000c, "KeyQ", US_Q), // qQ + DOM_CODE(0x070015, 0x0013, 0x001b, 0x0013, 0x000f, "KeyR", US_R), // rR + DOM_CODE(0x070016, 0x001f, 0x0027, 0x001f, 0x0001, "KeyS", US_S), // sS + DOM_CODE(0x070017, 0x0014, 0x001c, 0x0014, 0x0011, "KeyT", US_T), // tT + + DOM_CODE(0x070018, 0x0016, 0x001e, 0x0016, 0x0020, "KeyU", US_U), // uU + DOM_CODE(0x070019, 0x002f, 0x0037, 0x002f, 0x0009, "KeyV", US_V), // vV + DOM_CODE(0x07001a, 0x0011, 0x0019, 0x0011, 0x000d, "KeyW", US_W), // wW + DOM_CODE(0x07001b, 0x002d, 0x0035, 0x002d, 0x0007, "KeyX", US_X), // xX + DOM_CODE(0x07001c, 0x0015, 0x001d, 0x0015, 0x0010, "KeyY", US_Y), // yY + DOM_CODE(0x07001d, 0x002c, 0x0034, 0x002c, 0x0006, "KeyZ", US_Z), // zZ + DOM_CODE(0x07001e, 0x0002, 0x000a, 0x0002, 0x0012, "Digit1", DIGIT1), // 1! + DOM_CODE(0x07001f, 0x0003, 0x000b, 0x0003, 0x0013, "Digit2", DIGIT2), // 2@ + + DOM_CODE(0x070020, 0x0004, 0x000c, 0x0004, 0x0014, "Digit3", DIGIT3), // 3# + DOM_CODE(0x070021, 0x0005, 0x000d, 0x0005, 0x0015, "Digit4", DIGIT4), // 4$ + DOM_CODE(0x070022, 0x0006, 0x000e, 0x0006, 0x0017, "Digit5", DIGIT5), // 5% + DOM_CODE(0x070023, 0x0007, 0x000f, 0x0007, 0x0016, "Digit6", DIGIT6), // 6^ + DOM_CODE(0x070024, 0x0008, 0x0010, 0x0008, 0x001a, "Digit7", DIGIT7), // 7& + DOM_CODE(0x070025, 0x0009, 0x0011, 0x0009, 0x001c, "Digit8", DIGIT8), // 8* + DOM_CODE(0x070026, 0x000a, 0x0012, 0x000a, 0x0019, "Digit9", DIGIT9), // 9( + DOM_CODE(0x070027, 0x000b, 0x0013, 0x000b, 0x001d, "Digit0", DIGIT0), // 0) + + DOM_CODE(0x070028, 0x001c, 0x0024, 0x001c, 0x0024, "Enter", ENTER), + DOM_CODE(0x070029, 0x0001, 0x0009, 0x0001, 0x0035, "Escape", ESCAPE), + DOM_CODE(0x07002a, 0x000e, 0x0016, 0x000e, 0x0033, "Backspace", BACKSPACE), + DOM_CODE(0x07002b, 0x000f, 0x0017, 0x000f, 0x0030, "Tab", TAB), + DOM_CODE(0x07002c, 0x0039, 0x0041, 0x0039, 0x0031, "Space", SPACE), // Spacebar + DOM_CODE(0x07002d, 0x000c, 0x0014, 0x000c, 0x001b, "Minus", MINUS), // -_ + DOM_CODE(0x07002e, 0x000d, 0x0015, 0x000d, 0x0018, "Equal", EQUAL), // =+ + DOM_CODE(0x07002f, 0x001a, 0x0022, 0x001a, 0x0021, "BracketLeft", BRACKET_LEFT), + + DOM_CODE(0x070030, 0x001b, 0x0023, 0x001b, 0x001e, "BracketRight", BRACKET_RIGHT), + DOM_CODE(0x070031, 0x002b, 0x0033, 0x002b, 0x002a, "Backslash", BACKSLASH), // \| + // USB#070032 never appears on keyboards that have USB#070031. + // Platforms use the same scancode as for the two keys. + // Hence this code can only be generated synthetically + // (e.g. in a DOM Level 3 KeyboardEvent). + // The keycap varies on international keyboards: + // Dan: '* Dutch: <> Ger: #' UK: #~ + // TODO(garykac): Verify Mac intl keyboard. + //DOM_CODE(0x070032, 0x0000, 0x0000, 0x0000, 0xffff, NULL, INTL_HASH), + DOM_CODE(0x070033, 0x0027, 0x002f, 0x0027, 0x0029, "Semicolon", SEMICOLON), // ;: + DOM_CODE(0x070034, 0x0028, 0x0030, 0x0028, 0x0027, "Quote", QUOTE), // '" + DOM_CODE(0x070035, 0x0029, 0x0031, 0x0029, 0x0032, "Backquote", BACKQUOTE), // `~ + DOM_CODE(0x070036, 0x0033, 0x003b, 0x0033, 0x002b, "Comma", COMMA), // ,< + DOM_CODE(0x070037, 0x0034, 0x003c, 0x0034, 0x002f, "Period", PERIOD), // .> + + DOM_CODE(0x070038, 0x0035, 0x003d, 0x0035, 0x002c, "Slash", SLASH), // /? + // TODO(garykac): CapsLock requires special handling for each platform. + DOM_CODE(0x070039, 0x003a, 0x0042, 0x003a, 0x0039, "CapsLock", CAPS_LOCK), + DOM_CODE(0x07003a, 0x003b, 0x0043, 0x003b, 0x007a, "F1", F1), + DOM_CODE(0x07003b, 0x003c, 0x0044, 0x003c, 0x0078, "F2", F2), + DOM_CODE(0x07003c, 0x003d, 0x0045, 0x003d, 0x0063, "F3", F3), + DOM_CODE(0x07003d, 0x003e, 0x0046, 0x003e, 0x0076, "F4", F4), + DOM_CODE(0x07003e, 0x003f, 0x0047, 0x003f, 0x0060, "F5", F5), + DOM_CODE(0x07003f, 0x0040, 0x0048, 0x0040, 0x0061, "F6", F6), + + DOM_CODE(0x070040, 0x0041, 0x0049, 0x0041, 0x0062, "F7", F7), + DOM_CODE(0x070041, 0x0042, 0x004a, 0x0042, 0x0064, "F8", F8), + DOM_CODE(0x070042, 0x0043, 0x004b, 0x0043, 0x0065, "F9", F9), + DOM_CODE(0x070043, 0x0044, 0x004c, 0x0044, 0x006d, "F10", F10), + DOM_CODE(0x070044, 0x0057, 0x005f, 0x0057, 0x0067, "F11", F11), + DOM_CODE(0x070045, 0x0058, 0x0060, 0x0058, 0x006f, "F12", F12), + // PrintScreen is effectively F13 on Mac OS X. + DOM_CODE(0x070046, 0x0063, 0x006b, 0xe037, 0xffff, "PrintScreen", PRINT_SCREEN), + DOM_CODE(0x070047, 0x0046, 0x004e, 0x0046, 0xffff, "ScrollLock", SCROLL_LOCK), + + DOM_CODE(0x070048, 0x0077, 0x007f, 0x0045, 0xffff, "Pause", PAUSE), + // USB#0x070049 Insert, labeled "Help/Insert" on Mac -- see note M1 at top. + DOM_CODE(0x070049, 0x006e, 0x0076, 0xe052, 0x0072, "Insert", INSERT), + DOM_CODE(0x07004a, 0x0066, 0x006e, 0xe047, 0x0073, "Home", HOME), + DOM_CODE(0x07004b, 0x0068, 0x0070, 0xe049, 0x0074, "PageUp", PAGE_UP), + // Delete (Forward Delete) named DEL because DELETE conflicts with + DOM_CODE(0x07004c, 0x006f, 0x0077, 0xe053, 0x0075, "Delete", DEL), + DOM_CODE(0x07004d, 0x006b, 0x0073, 0xe04f, 0x0077, "End", END), + DOM_CODE(0x07004e, 0x006d, 0x0075, 0xe051, 0x0079, "PageDown", PAGE_DOWN), + DOM_CODE(0x07004f, 0x006a, 0x0072, 0xe04d, 0x007c, "ArrowRight", ARROW_RIGHT), + + DOM_CODE(0x070050, 0x0069, 0x0071, 0xe04b, 0x007b, "ArrowLeft", ARROW_LEFT), + DOM_CODE(0x070051, 0x006c, 0x0074, 0xe050, 0x007d, "ArrowDown", ARROW_DOWN), + DOM_CODE(0x070052, 0x0067, 0x006f, 0xe048, 0x007e, "ArrowUp", ARROW_UP), + DOM_CODE(0x070053, 0x0045, 0x004d, 0xe045, 0x0047, "NumLock", NUM_LOCK), + DOM_CODE(0x070054, 0x0062, 0x006a, 0xe035, 0x004b, "NumpadDivide", NUMPAD_DIVIDE), + DOM_CODE(0x070055, 0x0037, 0x003f, 0x0037, 0x0043, "NumpadMultiply", + NUMPAD_MULTIPLY), // Keypad_* + DOM_CODE(0x070056, 0x004a, 0x0052, 0x004a, 0x004e, "NumpadSubtract", + NUMPAD_SUBTRACT), // Keypad_- + DOM_CODE(0x070057, 0x004e, 0x0056, 0x004e, 0x0045, "NumpadAdd", NUMPAD_ADD), + + DOM_CODE(0x070058, 0x0060, 0x0068, 0xe01c, 0x004c, "NumpadEnter", NUMPAD_ENTER), + DOM_CODE(0x070059, 0x004f, 0x0057, 0x004f, 0x0053, "Numpad1", NUMPAD1), // +End + DOM_CODE(0x07005a, 0x0050, 0x0058, 0x0050, 0x0054, "Numpad2", NUMPAD2), // +Down + DOM_CODE(0x07005b, 0x0051, 0x0059, 0x0051, 0x0055, "Numpad3", NUMPAD3), // +PageDn + DOM_CODE(0x07005c, 0x004b, 0x0053, 0x004b, 0x0056, "Numpad4", NUMPAD4), // +Left + DOM_CODE(0x07005d, 0x004c, 0x0054, 0x004c, 0x0057, "Numpad5", NUMPAD5), // + DOM_CODE(0x07005e, 0x004d, 0x0055, 0x004d, 0x0058, "Numpad6", NUMPAD6), // +Right + DOM_CODE(0x07005f, 0x0047, 0x004f, 0x0047, 0x0059, "Numpad7", NUMPAD7), // +Home + + DOM_CODE(0x070060, 0x0048, 0x0050, 0x0048, 0x005b, "Numpad8", NUMPAD8), // +Up + DOM_CODE(0x070061, 0x0049, 0x0051, 0x0049, 0x005c, "Numpad9", NUMPAD9), // +PageUp + DOM_CODE(0x070062, 0x0052, 0x005a, 0x0052, 0x0052, "Numpad0", NUMPAD0), // +Insert + DOM_CODE(0x070063, 0x0053, 0x005b, 0x0053, 0x0041, "NumpadDecimal", + NUMPAD_DECIMAL), // Keypad_. Delete + // USB#070064 is not present on US keyboard. + // This key is typically located near LeftShift key. + // The keycap varies on international keyboards: + // Dan: <> Dutch: ][ Ger: <> UK: \| + DOM_CODE(0x070064, 0x0056, 0x005e, 0x0056, 0x000a, "IntlBackslash", INTL_BACKSLASH), + // USB#0x070065 Application Menu (next to RWin key) -- see note L2 at top. + DOM_CODE(0x070065, 0x007f, 0x0087, 0xe05d, 0x006e, "ContextMenu", CONTEXT_MENU), + DOM_CODE(0x070066, 0x0074, 0x007c, 0xe05e, 0xffff, "Power", POWER), + DOM_CODE(0x070067, 0x0075, 0x007d, 0x0059, 0x0051, "NumpadEqual", NUMPAD_EQUAL), + + DOM_CODE(0x070068, 0x00b7, 0x00bf, 0x0064, 0x0069, "F13", F13), + DOM_CODE(0x070069, 0x00b8, 0x00c0, 0x0065, 0x006b, "F14", F14), + DOM_CODE(0x07006a, 0x00b9, 0x00c1, 0x0066, 0x0071, "F15", F15), + DOM_CODE(0x07006b, 0x00ba, 0x00c2, 0x0067, 0x006a, "F16", F16), + DOM_CODE(0x07006c, 0x00bb, 0x00c3, 0x0068, 0x0040, "F17", F17), + DOM_CODE(0x07006d, 0x00bc, 0x00c4, 0x0069, 0x004f, "F18", F18), + DOM_CODE(0x07006e, 0x00bd, 0x00c5, 0x006a, 0x0050, "F19", F19), + DOM_CODE(0x07006f, 0x00be, 0x00c6, 0x006b, 0x005a, "F20", F20), + + DOM_CODE(0x070070, 0x00bf, 0x00c7, 0x006c, 0xffff, "F21", F21), + DOM_CODE(0x070071, 0x00c0, 0x00c8, 0x006d, 0xffff, "F22", F22), + DOM_CODE(0x070072, 0x00c1, 0x00c9, 0x006e, 0xffff, "F23", F23), + // USB#0x070073 -- see note W1 at top. + DOM_CODE(0x070073, 0x00c2, 0x00ca, 0x0076, 0xffff, "F24", F24), + DOM_CODE(0x070074, 0x0086, 0x008e, 0x0000, 0xffff, "Open", OPEN), // Execute + // USB#0x070075 Help -- see note M1 at top. + DOM_CODE(0x070075, 0x008a, 0x0092, 0xe03b, 0xffff, "Help", HELP), + // USB#0x070076 Keyboard Menu -- see note L2 at top. + //DOM_CODE(0x070076, 0x0000, 0x0000, 0x0000, 0xffff, NULL, MENU), // Menu + DOM_CODE(0x070077, 0x0084, 0x008c, 0x0000, 0xffff, "Select", SELECT), // Select + + //DOM_CODE(0x070078, 0x0080, 0x0088, 0x0000, 0xffff, NULL, STOP), // Stop + DOM_CODE(0x070079, 0x0081, 0x0089, 0x0000, 0xffff, "Again", AGAIN), // Again + DOM_CODE(0x07007a, 0x0083, 0x008b, 0xe008, 0xffff, "Undo", UNDO), + DOM_CODE(0x07007b, 0x0089, 0x0091, 0xe017, 0xffff, "Cut", CUT), + DOM_CODE(0x07007c, 0x0085, 0x008d, 0xe018, 0xffff, "Copy", COPY), + DOM_CODE(0x07007d, 0x0087, 0x008f, 0xe00a, 0xffff, "Paste", PASTE), + DOM_CODE(0x07007e, 0x0088, 0x0090, 0x0000, 0xffff, "Find", FIND), // Find + DOM_CODE(0x07007f, 0x0071, 0x0079, 0xe020, 0x004a, "AudioVolumeMute", VOLUME_MUTE), + + DOM_CODE(0x070080, 0x0073, 0x007b, 0xe030, 0x0048, "AudioVolumeUp", VOLUME_UP), + DOM_CODE(0x070081, 0x0072, 0x007a, 0xe02e, 0x0049, "AudioVolumeDown", VOLUME_DOWN), + //DOM_CODE(0x070082, 0x0000, 0x0000, 0x0000, 0xffff, NULL, LOCKING_CAPS_LOCK), + //DOM_CODE(0x070083, 0x0000, 0x0000, 0x0000, 0xffff, NULL, LOCKING_NUM_LOCK), + //DOM_CODE(0x070084, 0x0000, 0x0000, 0x0000, 0xffff, NULL, LOCKING_SCROLL_LOCK), + DOM_CODE(0x070085, 0x0079, 0x0081, 0x007e, 0x005f, "NumpadComma", NUMPAD_COMMA), + + // International1 + // USB#070086 is used on AS/400 keyboards. Standard Keypad_= is USB#070067. + //DOM_CODE(0x070086, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_EQUAL), + // USB#070087 is used for Brazilian /? and Japanese _ 'ro'. + DOM_CODE(0x070087, 0x0059, 0x0061, 0x0073, 0x005e, "IntlRo", INTL_RO), + // International2 + // USB#070088 is used as Japanese Hiragana/Katakana key. + DOM_CODE(0x070088, 0x005d, 0x0065, 0x0070, 0xffff, "KanaMode", KANA_MODE), + // International3 + // USB#070089 is used as Japanese Yen key. + DOM_CODE(0x070089, 0x007c, 0x0084, 0x007d, 0x005d, "IntlYen", INTL_YEN), + // International4 + // USB#07008a is used as Japanese Henkan (Convert) key. + DOM_CODE(0x07008a, 0x005c, 0x0064, 0x0079, 0xffff, "Convert", CONVERT), + // International5 + // USB#07008b is used as Japanese Muhenkan (No-convert) key. + DOM_CODE(0x07008b, 0x005e, 0x0066, 0x007b, 0xffff, "NonConvert", NON_CONVERT), + //DOM_CODE(0x07008c, 0x005f, 0x0067, 0x005c, 0xffff, NULL, INTERNATIONAL6), + //DOM_CODE(0x07008d, 0x0000, 0x0000, 0x0000, 0xffff, NULL, INTERNATIONAL7), + //DOM_CODE(0x07008e, 0x0000, 0x0000, 0x0000, 0xffff, NULL, INTERNATIONAL8), + //DOM_CODE(0x07008f, 0x0000, 0x0000, 0x0000, 0xffff, NULL, INTERNATIONAL9), + + // LANG1 + // USB#070090 is used as Korean Hangul/English toggle key, and as the Kana key + // on the Apple Japanese keyboard. + DOM_CODE(0x070090, 0x007a, 0x0082, 0x0072, 0x0068, "Lang1", LANG1), + // LANG2 + // USB#070091 is used as Korean Hanja conversion key, and as the Eisu key on + // the Apple Japanese keyboard. + DOM_CODE(0x070091, 0x007b, 0x0083, 0x0071, 0x0066, "Lang2", LANG2), + // LANG3 + // USB#070092 is used as Japanese Katakana key. + DOM_CODE(0x070092, 0x005a, 0x0062, 0x0078, 0xffff, "Lang3", LANG3), + // LANG4 + // USB#070093 is used as Japanese Hiragana key. + DOM_CODE(0x070093, 0x005b, 0x0063, 0x0077, 0xffff, "Lang4", LANG4), + // LANG5 + // USB#070094 is used as Japanese Zenkaku/Hankaku (Fullwidth/halfwidth) key. + // Not mapped on Windows -- see note W1 at top. + DOM_CODE(0x070094, 0x0055, 0x005d, 0x0000, 0xffff, "Lang5", LANG5), + //DOM_CODE(0x070095, 0x0000, 0x0000, 0x0000, 0xffff, NULL, LANG6), // LANG6 + //DOM_CODE(0x070096, 0x0000, 0x0000, 0x0000, 0xffff, NULL, LANG7), // LANG7 + //DOM_CODE(0x070097, 0x0000, 0x0000, 0x0000, 0xffff, NULL, LANG8), // LANG8 + //DOM_CODE(0x070098, 0x0000, 0x0000, 0x0000, 0xffff, NULL, LANG9), // LANG9 + + //DOM_CODE(0x070099, 0x0000, 0x0000, 0x0000, 0xffff, NULL, ALTERNATE_ERASE), + //DOM_CODE(0x07009a, 0x0000, 0x0000, 0x0000, 0xffff, NULL, SYS_REQ), // /Attention + DOM_CODE(0x07009b, 0x0000, 0x0000, 0x0000, 0xffff, "Abort", ABORT), // Cancel + // USB#0x07009c Keyboard Clear -- see note L1 at top. + //DOM_CODE(0x07009c, 0x0000, 0x0000, 0x0000, 0xffff, NULL, CLEAR), // Clear + //DOM_CODE(0x07009d, 0x0000, 0x0000, 0x0000, 0xffff, NULL, PRIOR), // Prior + //DOM_CODE(0x07009e, 0x0000, 0x0000, 0x0000, 0xffff, NULL, RETURN), // Return + //DOM_CODE(0x07009f, 0x0000, 0x0000, 0x0000, 0xffff, NULL, SEPARATOR), // Separator + + //DOM_CODE(0x0700a0, 0x0000, 0x0000, 0x0000, 0xffff, NULL, OUT), // Out + //DOM_CODE(0x0700a1, 0x0000, 0x0000, 0x0000, 0xffff, NULL, OPER), // Oper + //DOM_CODE(0x0700a2, 0x0000, 0x0000, 0x0000, 0xffff, NULL, CLEAR_AGAIN), + // USB#0x0700a3 Props -- see note L2 at top. + DOM_CODE(0x0700a3, 0x0000, 0x0000, 0x0000, 0xffff, "Props", PROPS), // CrSel/Props + //DOM_CODE(0x0700a4, 0x0000, 0x0000, 0x0000, 0xffff, NULL, EX_SEL), // ExSel + + //DOM_CODE(0x0700b0, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_00), + //DOM_CODE(0x0700b1, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_000), + //DOM_CODE(0x0700b2, 0x0000, 0x0000, 0x0000, 0xffff, NULL, THOUSANDS_SEPARATOR), + //DOM_CODE(0x0700b3, 0x0000, 0x0000, 0x0000, 0xffff, NULL, DECIMAL_SEPARATOR), + //DOM_CODE(0x0700b4, 0x0000, 0x0000, 0x0000, 0xffff, NULL, CURRENCY_UNIT), + //DOM_CODE(0x0700b5, 0x0000, 0x0000, 0x0000, 0xffff, NULL, CURRENCY_SUBUNIT), + DOM_CODE(0x0700b6, 0x00b3, 0x00bb, 0x0000, 0xffff, "NumpadParenLeft", + NUMPAD_PAREN_LEFT), // Keypad_( + DOM_CODE(0x0700b7, 0x00b4, 0x00bc, 0x0000, 0xffff, "NumpadParenRight", + NUMPAD_PAREN_RIGHT), // Keypad_) + + //DOM_CODE(0x0700b8, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_BRACE_LEFT), + //DOM_CODE(0x0700b9, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_BRACE_RIGHT), + //DOM_CODE(0x0700ba, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_TAB), + DOM_CODE(0x0700bb, 0x0000, 0x0000, 0x0000, 0xffff, "NumpadBackspace", + NUMPAD_BACKSPACE), // Keypad_Backspace + //DOM_CODE(0x0700bc, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_A), + //DOM_CODE(0x0700bd, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_B), + //DOM_CODE(0x0700be, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_C), + //DOM_CODE(0x0700bf, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_D), + + //DOM_CODE(0x0700c0, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_E), + //DOM_CODE(0x0700c1, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_F), + //DOM_CODE(0x0700c2, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_XOR), + //DOM_CODE(0x0700c3, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_CARAT), + //DOM_CODE(0x0700c4, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_PERCENT), + //DOM_CODE(0x0700c5, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_LESS_THAN), + //DOM_CODE(0x0700c6, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_GREATER_THAN), + //DOM_CODE(0x0700c7, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_AMERSAND), + + //DOM_CODE(0x0700c8, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_DOUBLE_AMPERSAND), + //DOM_CODE(0x0700c9, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_VERTICAL_BAR), + //DOM_CODE(0x0700ca, 0x0000, 0x0000, 0x0000, 0xffff, NULL, + // NUMPAD_DOUBLE_VERTICAL_BAR), // Keypad_|| + //DOM_CODE(0x0700cb, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_COLON), + //DOM_CODE(0x0700cc, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_NUMBER), + //DOM_CODE(0x0700cd, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_SPACE), + //DOM_CODE(0x0700ce, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_AT), + //DOM_CODE(0x0700cf, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_EXCLAMATION), + + DOM_CODE(0x0700d0, 0x0000, 0x0000, 0x0000, 0xffff, "NumpadMemoryStore", + NUMPAD_MEMORY_STORE), // Keypad_MemoryStore + DOM_CODE(0x0700d1, 0x0000, 0x0000, 0x0000, 0xffff, "NumpadMemoryRecall", + NUMPAD_MEMORY_RECALL), // Keypad_MemoryRecall + DOM_CODE(0x0700d2, 0x0000, 0x0000, 0x0000, 0xffff, "NumpadMemoryClear", + NUMPAD_MEMORY_CLEAR), // Keypad_MemoryClear + DOM_CODE(0x0700d3, 0x0000, 0x0000, 0x0000, 0xffff, "NumpadMemoryAdd", + NUMPAD_MEMORY_ADD), // Keypad_MemoryAdd + DOM_CODE(0x0700d4, 0x0000, 0x0000, 0x0000, 0xffff, "NumpadMemorySubtract", + NUMPAD_MEMORY_SUBTRACT), // Keypad_MemorySubtract + //DOM_CODE(0x0700d5, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_MEMORY_MULTIPLE), + //DOM_CODE(0x0700d6, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_MEMORY_DIVIDE), + DOM_CODE(0x0700d7, 0x0076, 0x007e, 0x0000, 0xffff, NULL, NUMPAD_SIGN_CHANGE), // +/- + // USB#0x0700d8 Keypad Clear -- see note L1 at top. + DOM_CODE(0x0700d8, 0x0000, 0x0000, 0x0000, 0xffff, "NumpadClear", NUMPAD_CLEAR), + DOM_CODE(0x0700d9, 0x0000, 0x0000, 0x0000, 0xffff, "NumpadClearEntry", + NUMPAD_CLEAR_ENTRY), // Keypad_ClearEntry + //DOM_CODE(0x0700da, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_BINARY), + //DOM_CODE(0x0700db, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_OCTAL), + //DOM_CODE(0x0700dc, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_DECIMAL), + //DOM_CODE(0x0700dd, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NUMPAD_HEXADECIMAL), + + // USB#0700de - #0700df are reserved. + DOM_CODE(0x0700e0, 0x001d, 0x0025, 0x001d, 0x003b, "ControlLeft", CONTROL_LEFT), + DOM_CODE(0x0700e1, 0x002a, 0x0032, 0x002a, 0x0038, "ShiftLeft", SHIFT_LEFT), + // USB#0700e2: left Alt key (Mac left Option key). + DOM_CODE(0x0700e2, 0x0038, 0x0040, 0x0038, 0x003a, "AltLeft", ALT_LEFT), + // USB#0700e3: left GUI key, e.g. Windows, Mac Command, ChromeOS Search. + DOM_CODE(0x0700e3, 0x007d, 0x0085, 0xe05b, 0x0037, "MetaLeft", META_LEFT), + DOM_CODE(0x0700e4, 0x0061, 0x0069, 0xe01d, 0x003e, "ControlRight", CONTROL_RIGHT), + DOM_CODE(0x0700e5, 0x0036, 0x003e, 0x0036, 0x003c, "ShiftRight", SHIFT_RIGHT), + // USB#0700e6: right Alt key (Mac right Option key). + DOM_CODE(0x0700e6, 0x0064, 0x006c, 0xe038, 0x003d, "AltRight", ALT_RIGHT), + // USB#0700e7: right GUI key, e.g. Windows, Mac Command, ChromeOS Search. + DOM_CODE(0x0700e7, 0x007e, 0x0086, 0xe05c, 0x0036, "MetaRight", META_RIGHT), + + // USB#0700e8 - #07ffff are reserved + + // ================================== + // USB Usage Page 0x0c: Consumer Page + // ================================== + // AL = Application Launch + // AC = Application Control + + // TODO(garykac): Many XF86 keys have multiple scancodes mapping to them. + // We need to map all of these into a canonical USB scancode without + // confusing the reverse-lookup - most likely by simply returning the first + // found match. + + // TODO(garykac): Find appropriate mappings for: + // Win#e03c Music - USB#0c0193 is AL_AVCapturePlayback + // Win#e064 Pictures + // XKB#0080 XF86LaunchA + // XKB#0099 XF86Send + // XKB#009b XF86Xfer + // XKB#009c XF86Launch1 + // XKB#009d XF86Launch2 + // XKB... remaining XF86 keys + + // KEY_BRIGHTNESS* added in Linux 3.16 + // http://www.usb.org/developers/hidpage/HUTRR41.pdf + // + // Keyboard backlight/illumination spec update. + // https://www.usb.org/sites/default/files/hutrr73_-_fn_key_and_keyboard_backlight_brightness_0.pdf + // USB evdev XKB Win Mac Code + DOM_CODE(0x0c0060, 0x0166, 0x016e, 0x0000, 0xffff, NULL, INFO), + DOM_CODE(0x0c0061, 0x0172, 0x017a, 0x0000, 0xffff, NULL, CLOSED_CAPTION_TOGGLE), + DOM_CODE(0x0c006f, 0x00e1, 0x00e9, 0x0000, 0xffff, "BrightnessUp", BRIGHTNESS_UP), + DOM_CODE(0x0c0070, 0x00e0, 0x00e8, 0x0000, 0xffff, "BrightnessDown", + BRIGHTNESS_DOWN), // Display Brightness Decrement + DOM_CODE(0x0c0072, 0x01af, 0x01b7, 0x0000, 0xffff, NULL, BRIGHTNESS_TOGGLE), + DOM_CODE(0x0c0073, 0x0250, 0x0258, 0x0000, 0xffff, NULL, BRIGHTNESS_MINIMIUM), + DOM_CODE(0x0c0074, 0x0251, 0x0259, 0x0000, 0xffff, NULL, BRIGHTNESS_MAXIMUM), + DOM_CODE(0x0c0075, 0x00f4, 0x00fc, 0x0000, 0xffff, NULL, BRIGHTNESS_AUTO), + DOM_CODE(0x0c0079, 0x00e6, 0x00ee, 0x0000, 0xffff, NULL, KBD_ILLUM_UP), + DOM_CODE(0x0c007a, 0x00e5, 0x00ed, 0x0000, 0xffff, NULL, KBD_ILLUM_DOWN), + DOM_CODE(0x0c0083, 0x0195, 0x019d, 0x0000, 0xffff, NULL, MEDIA_LAST), + DOM_CODE(0x0c008c, 0x00a9, 0x00b1, 0x0000, 0xffff, NULL, LAUNCH_PHONE), + DOM_CODE(0x0c008d, 0x016a, 0x0172, 0x0000, 0xffff, NULL, PROGRAM_GUIDE), + DOM_CODE(0x0c0094, 0x00ae, 0x00b6, 0x0000, 0xffff, NULL, EXIT), + DOM_CODE(0x0c009c, 0x019a, 0x01a2, 0x0000, 0xffff, NULL, CHANNEL_UP), + DOM_CODE(0x0c009d, 0x019b, 0x01a3, 0x0000, 0xffff, NULL, CHANNEL_DOWN), + + // USB evdev XKB Win Mac + DOM_CODE(0x0c00b0, 0x00cf, 0x00d7, 0x0000, 0xffff, "MediaPlay", MEDIA_PLAY), + DOM_CODE(0x0c00b1, 0x00c9, 0x00d1, 0x0000, 0xffff, "MediaPause", MEDIA_PAUSE), + DOM_CODE(0x0c00b2, 0x00a7, 0x00af, 0x0000, 0xffff, "MediaRecord", MEDIA_RECORD), + DOM_CODE(0x0c00b3, 0x00d0, 0x00d8, 0x0000, 0xffff, "MediaFastForward", MEDIA_FAST_FORWARD), + DOM_CODE(0x0c00b4, 0x00a8, 0x00b0, 0x0000, 0xffff, "MediaRewind", MEDIA_REWIND), + DOM_CODE(0x0c00b5, 0x00a3, 0x00ab, 0xe019, 0xffff, "MediaTrackNext", + MEDIA_TRACK_NEXT), + DOM_CODE(0x0c00b6, 0x00a5, 0x00ad, 0xe010, 0xffff, "MediaTrackPrevious", + MEDIA_TRACK_PREVIOUS), + DOM_CODE(0x0c00b7, 0x00a6, 0x00ae, 0xe024, 0xffff, "MediaStop", MEDIA_STOP), + DOM_CODE(0x0c00b8, 0x00a1, 0x00a9, 0xe02c, 0xffff, "Eject", EJECT), + DOM_CODE(0x0c00cd, 0x00a4, 0x00ac, 0xe022, 0xffff, "MediaPlayPause", + MEDIA_PLAY_PAUSE), + DOM_CODE(0x0c00cf, 0x0246, 0x024e, 0x0000, 0xffff, NULL, SPEECH_INPUT_TOGGLE), + DOM_CODE(0x0c00e5, 0x00d1, 0x00d9, 0x0000, 0xffff, NULL, BASS_BOOST), + //DOM_CODE(0x0c00e6, 0x0000, 0x0000, 0x0000, 0xffff, NULL, SURROUND_MODE), + //DOM_CODE(0x0c0150, 0x0000, 0x0000, 0x0000, 0xffff, NULL, AUDIO_BALANCE_RIGHT), + //DOM_CODE(0x0c0151, 0x0000, 0x0000, 0x0000, 0xffff, NULL, AUDIO_BALANCE_LEFT ), + //DOM_CODE(0x0c0152, 0x0000, 0x0000, 0x0000, 0xffff, NULL, AUDIO_BASS_INCREMENT), + //DOM_CODE(0x0c0153, 0x0000, 0x0000, 0x0000, 0xffff, NULL, AUDIO_BASS_DECREMENT), + //DOM_CODE(0x0c0154, 0x0000, 0x0000, 0x0000, 0xffff, NULL, AUDIO_TREBLE_INCREMENT), + //DOM_CODE(0x0c0155, 0x0000, 0x0000, 0x0000, 0xffff, NULL, AUDIO_TREBLE_DECREMENT), + // USB#0c0183: AL Consumer Control Configuration + DOM_CODE(0x0c0183, 0x00ab, 0x00b3, 0xe06d, 0xffff, "MediaSelect", MEDIA_SELECT), + DOM_CODE(0x0c0184, 0x01a5, 0x01ad, 0x0000, 0xffff, NULL, LAUNCH_WORD_PROCESSOR), + DOM_CODE(0x0c0186, 0x01a7, 0x01af, 0x0000, 0xffff, NULL, LAUNCH_SPREADSHEET), + // USB#0x0c018a AL_EmailReader + DOM_CODE(0x0c018a, 0x009b, 0x00a3, 0xe06c, 0xffff, "LaunchMail", LAUNCH_MAIL), + // USB#0x0c018d: AL Contacts/Address Book + DOM_CODE(0x0c018d, 0x01ad, 0x01b5, 0x0000, 0xffff, NULL, LAUNCH_CONTACTS), + // USB#0x0c018e: AL Calendar/Schedule + DOM_CODE(0x0c018e, 0x018d, 0x0195, 0x0000, 0xffff, NULL, LAUNCH_CALENDAR), + // USB#0x0c018f AL Task/Project Manager + //DOM_CODE(0x0c018f, 0x0241, 0x0249, 0x0000, 0xffff, NULL, LAUNCH_TASK_MANAGER), + // USB#0x0c0190: AL Log/Journal/Timecard + //DOM_CODE(0x0c0190, 0x0242, 0x024a, 0x0000, 0xffff, NULL, LAUNCH_LOG), + // USB#0x0c0192: AL_Calculator + DOM_CODE(0x0c0192, 0x008c, 0x0094, 0xe021, 0xffff, "LaunchApp2", LAUNCH_APP2), + // USB#0c0194: My Computer (AL_LocalMachineBrowser) + DOM_CODE(0x0c0194, 0x0090, 0x0098, 0xe06b, 0xffff, "LaunchApp1", LAUNCH_APP1), + DOM_CODE(0x0c0196, 0x0096, 0x009e, 0x0000, 0xffff, NULL, LAUNCH_INTERNET_BROWSER), + DOM_CODE(0x0c019C, 0x01b1, 0x01b9, 0x0000, 0xffff, NULL, LOG_OFF), + // USB#0x0c019e: AL Terminal Lock/Screensaver + DOM_CODE(0x0c019e, 0x0098, 0x00a0, 0x0000, 0xffff, NULL, LOCK_SCREEN), + // USB#0x0c019f AL Control Panel + DOM_CODE(0x0c019f, 0x0243, 0x024b, 0x0000, 0xffff, "LaunchControlPanel", + LAUNCH_CONTROL_PANEL), + // USB#0x0c01a2: AL Select Task/Application + DOM_CODE(0x0c01a2, 0x0244, 0x024c, 0x0000, 0xffff, "SelectTask", SELECT_TASK), + // USB#0x0c01a7: AL_Documents + DOM_CODE(0x0c01a7, 0x00eb, 0x00f3, 0x0000, 0xffff, NULL, LAUNCH_DOCUMENTS), + DOM_CODE(0x0c01ab, 0x01b0, 0x01b8, 0x0000, 0xffff, NULL, SPELL_CHECK), + // USB#0x0c01ae: AL Keyboard Layout + DOM_CODE(0x0c01ae, 0x0176, 0x017e, 0x0000, 0xffff, NULL, LAUNCH_KEYBOARD_LAYOUT), + DOM_CODE(0x0c01b1, 0x0245, 0x024d, 0x0000, 0xffff, "LaunchScreenSaver", + LAUNCH_SCREEN_SAVER), // AL Screen Saver + DOM_CODE(0x0c01cb, 0x0247, 0x024f, 0x0000, 0xffff, "LaunchAssistant", + LAUNCH_ASSISTANT), // AL Context-aware desktop assistant + // USB#0c01b4: Home Directory (AL_FileBrowser) (Explorer) + //DOM_CODE(0x0c01b4, 0x0000, 0x0000, 0x0000, 0xffff, NULL, LAUNCH_FILE_BROWSER), + // USB#0x0c01b7: AL Audio Browser + DOM_CODE(0x0c01b7, 0x0188, 0x0190, 0x0000, 0xffff, NULL, LAUNCH_AUDIO_BROWSER), + // USB#0x0c0201: AC New + DOM_CODE(0x0c0201, 0x00b5, 0x00bd, 0x0000, 0xffff, NULL, NEW), + // USB#0x0c0203: AC Close + DOM_CODE(0x0c0203, 0x00ce, 0x00d6, 0x0000, 0xffff, NULL, CLOSE), + // USB#0x0c0207: AC Close + DOM_CODE(0x0c0207, 0x00ea, 0x00f2, 0x0000, 0xffff, NULL, SAVE), + // USB#0x0c0208: AC Print + DOM_CODE(0x0c0208, 0x00d2, 0x00da, 0x0000, 0xffff, NULL, PRINT), + // USB#0x0c0221: AC_Search + DOM_CODE(0x0c0221, 0x00d9, 0x00e1, 0xe065, 0xffff, "BrowserSearch", BROWSER_SEARCH), + // USB#0x0c0223: AC_Home + DOM_CODE(0x0c0223, 0x00ac, 0x00b4, 0xe032, 0xffff, "BrowserHome", BROWSER_HOME), + // USB#0x0c0224: AC_Back + DOM_CODE(0x0c0224, 0x009e, 0x00a6, 0xe06a, 0xffff, "BrowserBack", BROWSER_BACK), + // USB#0x0c0225: AC_Forward + DOM_CODE(0x0c0225, 0x009f, 0x00a7, 0xe069, 0xffff, "BrowserForward", + BROWSER_FORWARD), + // USB#0x0c0226: AC_Stop + DOM_CODE(0x0c0226, 0x0080, 0x0088, 0xe068, 0xffff, "BrowserStop", BROWSER_STOP), + // USB#0x0c0227: AC_Refresh (Reload) + DOM_CODE(0x0c0227, 0x00ad, 0x00b5, 0xe067, 0xffff, "BrowserRefresh", + BROWSER_REFRESH), + // USB#0x0c022a: AC_Bookmarks (Favorites) + DOM_CODE(0x0c022a, 0x009c, 0x00a4, 0xe066, 0xffff, "BrowserFavorites", + BROWSER_FAVORITES), + DOM_CODE(0x0c022d, 0x01a2, 0x01aa, 0x0000, 0xffff, NULL, ZOOM_IN), + DOM_CODE(0x0c022e, 0x01a3, 0x01ab, 0x0000, 0xffff, NULL, ZOOM_OUT), + // USB#0x0c0230: AC Full Screen View + //DOM_CODE(0x0c0230, 0x0000, 0x0000, 0x0000, 0xffff, NULL, ZOOM_FULL), + // USB#0x0c0231: AC Normal View + //DOM_CODE(0x0c0231, 0x0000, 0x0000, 0x0000, 0xffff, NULL, ZOOM_NORMAL), + DOM_CODE(0x0c0232, 0x0174, 0x017c, 0x0000, 0xffff, "ZoomToggle", + ZOOM_TOGGLE), // AC View Toggle + // USB#0x0c0279: AC Redo/Repeat + DOM_CODE(0x0c0279, 0x00b6, 0x00be, 0x0000, 0xffff, NULL, REDO), + // USB#0x0c0289: AC_Reply + DOM_CODE(0x0c0289, 0x00e8, 0x00f0, 0x0000, 0xffff, "MailReply", MAIL_REPLY), + // USB#0x0c028b: AC_ForwardMsg (MailForward) + DOM_CODE(0x0c028b, 0x00e9, 0x00f1, 0x0000, 0xffff, "MailForward", MAIL_FORWARD), + // USB#0x0c028c: AC_Send + DOM_CODE(0x0c028c, 0x00e7, 0x00ef, 0x0000, 0xffff, "MailSend", MAIL_SEND), + // USB#0x0c029d: AC Next Keyboard Layout Select + DOM_CODE(0x0c029d, 0x0248, 0x0250, 0x0000, 0xffff, "KeyboardLayoutSelect", + KEYBOARD_LAYOUT_SELECT), + DOM_CODE(0x0c029f, 0x0078, 0x0080, 0x0000, 0xffff, "ShowAllWindows", + SHOW_ALL_WINDOWS), // AC Desktop Show All Windows +}; diff --git a/html_chromium/chromium/keycode_converter.cc b/html_chromium/chromium/keycode_converter.cc new file mode 100644 index 0000000..97b9d2d --- /dev/null +++ b/html_chromium/chromium/keycode_converter.cc @@ -0,0 +1,73 @@ +// A stripped down version of https://github.com/chromium/chromium/blob/main/ui/events/keycodes/dom/keycode_converter.cc +// Last Updated - August 7, 2024: https://github.com/chromium/chromium/commit/24d53e3e9a861c76d0e1626522c93e371f5d9223 + +// Copyright 2013 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "keycode_converter.h" + +#include + +#include "dom_code.h" + +namespace ui { + +namespace { + +// Table of USB codes (equivalent to DomCode values), native scan codes, +// and DOM Level 3 |code| strings. +#if defined(_WIN32) +#define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \ + { usb, win, code } +#elif defined(__linux__) +#define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \ + { usb, xkb, code } +#elif defined(__APPLE__) +#define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \ + { usb, mac, code } +#else +#error Unsupported platform +#endif +#define DOM_CODE_DECLARATION constexpr KeycodeMapEntry kDomCodeMappings[] = +#include "dom_code_data.inc" +#undef DOM_CODE +#undef DOM_CODE_DECLARATION + +} // namespace + +// static +int KeycodeConverter::InvalidNativeKeycode() { + return kDomCodeMappings[0].native_keycode; +} + +// TODO(zijiehe): Most of the following functions can be optimized by using +// either multiple arrays or unordered_map. + +// static +int KeycodeConverter::DomCodeToNativeKeycode(DomCode code) { + return UsbKeycodeToNativeKeycode(static_cast(code)); +} + +// USB keycodes +// Note that USB keycodes are not part of any web standard. +// Please don't use USB keycodes in new code. + +// static +int KeycodeConverter::UsbKeycodeToNativeKeycode(uint32_t usb_keycode) { + // Deal with some special-cases that don't fit the 1:1 mapping. + if (usb_keycode == 0x070032) // non-US hash. + usb_keycode = 0x070031; // US backslash. +#ifdef __APPLE__ + if (usb_keycode == 0x070046) // PrintScreen. + usb_keycode = 0x070068; // F13. +#endif + + for (auto& mapping : kDomCodeMappings) { + if (mapping.usb_keycode == usb_keycode) + return mapping.native_keycode; + } + return InvalidNativeKeycode(); +} + +} // namespace ui diff --git a/html_chromium/chromium/keycode_converter.h b/html_chromium/chromium/keycode_converter.h new file mode 100644 index 0000000..103bcaf --- /dev/null +++ b/html_chromium/chromium/keycode_converter.h @@ -0,0 +1,70 @@ +// A stripped down version of https://github.com/chromium/chromium/blob/main/ui/events/keycodes/dom/keycode_converter.h +// Last Updated - August 7, 2024: https://github.com/chromium/chromium/commit/36619c19f1c497cb673f1a5d1b82e20da90663df + +// Copyright 2013 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_EVENTS_KEYCODES_DOM_KEYCODE_CONVERTER_H_ +#define UI_EVENTS_KEYCODES_DOM_KEYCODE_CONVERTER_H_ + +#include +#include + +#include + +#include "dom_code.h" + +// For reference, the W3C UI Event spec is located at: +// http://www.w3.org/TR/uievents/ + +namespace ui { + +// This structure is used to define the keycode mapping table. +// It is defined here because the unittests need access to it. +typedef struct { + // USB keycode: + // Upper 16-bits: USB Usage Page. + // Lower 16-bits: USB Usage Id: Assigned ID within this usage page. + uint32_t usb_keycode; + + // Contains one of the following: + // On Linux: XKB scancode + // On Windows: Windows OEM scancode + // On Mac: Mac keycode + // On Fuchsia: 16-bit Code from the USB Keyboard Usage Page. + int native_keycode; + + // The UIEvents (aka: DOM4Events) |code| value as defined in: + // http://www.w3.org/TR/DOM-Level-3-Events-code/ + const char* code; +} KeycodeMapEntry; + +// A class to convert between the current platform's native keycode (scancode) +// and platform-neutral |code| values (as defined in the W3C UI Events +// spec (http://www.w3.org/TR/uievents/). +class KeycodeConverter { + public: + KeycodeConverter() = delete; + KeycodeConverter(const KeycodeConverter&) = delete; + KeycodeConverter& operator=(const KeycodeConverter&) = delete; + + // Return the value that identifies an invalid native keycode. + static int InvalidNativeKeycode(); + + // Convert a DomCode into a native keycode. + static int DomCodeToNativeKeycode(DomCode code); + + // The following methods relate to USB keycodes. + // Note that USB keycodes are not part of any web standard. + // Please don't use USB keycodes in new code. + + // Conversion between USB keycode and native keycode values. + // Returns the invalid value if the supplied code is not recognized, + // or has no mapping. + static int UsbKeycodeToNativeKeycode(uint32_t usb_keycode); +}; + +} // namespace ui + +#endif // UI_EVENTS_KEYCODES_DOM_KEYCODE_CONVERTER_H_ diff --git a/html_chromium/chromium_process/CMakeLists.txt b/html_chromium/chromium_process/CMakeLists.txt deleted file mode 100644 index c08353e..0000000 --- a/html_chromium/chromium_process/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -set(SOURCES - ChromiumApp.cpp - ChromiumApp.h) - -if(UNIX AND NOT APPLE) - set(SOURCES ${SOURCES} Linux.cpp) -else() - message(FATAL_ERROR "No ChromiumApp Main implementation") -endif() - -# Lame -include_directories(${CEF_PATH}) - -add_executable(chromium_process ${SOURCES}) -target_link_libraries(chromium_process libcef_imp libcef_dll_wrapper) -install(TARGETS chromium_process RUNTIME DESTINATION ${GAME_BIN_DIR}) - diff --git a/html_chromium/chromium_process/ChromiumApp.cpp b/html_chromium/chromium_process/ChromiumApp.cpp deleted file mode 100644 index 5d6562d..0000000 --- a/html_chromium/chromium_process/ChromiumApp.cpp +++ /dev/null @@ -1,481 +0,0 @@ -#include "ChromiumApp.h" - -static bool V8ValueToCefValue( CefRefPtr outValue, const CefRefPtr& inValue, int depth = 0 ) -{ - if ( depth > 16 ) - return false; - - if ( inValue->IsNull() || inValue->IsUndefined() ) - { - outValue->SetNull(); - } - else if ( inValue->IsBool() ) - { - outValue->SetBool( inValue->GetBoolValue() ); - } - else if ( inValue->IsInt() || inValue->IsUInt() || inValue->IsDouble() ) - { - outValue->SetDouble( inValue->GetDoubleValue() ); - } - else if ( inValue->IsString() ) - { - outValue->SetString( inValue->GetStringValue() ); - } - else if ( inValue->IsArray() ) - { - int arrayLen = inValue->GetArrayLength(); - auto newList = CefListValue::Create(); - newList->SetSize( arrayLen ); - - for ( int i = 0; i < arrayLen; i++ ) - { - auto newValue = CefValue::Create(); - - if ( !V8ValueToCefValue( newValue, inValue->GetValue( i ), depth + 1 ) ) - return false; - - newList->SetValue( i, newValue ); - } - - outValue->SetList( newList ); - } - else if ( inValue->IsObject() ) - { - auto newMap = CefDictionaryValue::Create(); - std::vector keys; - inValue->GetKeys( keys ); - - for ( auto const& key : keys ) - { - auto newValue = CefValue::Create(); - - if ( !V8ValueToCefValue( newValue, inValue->GetValue( key ), depth + 1 ) ) - return false; - - newMap->SetValue( key, newValue ); - } - - outValue->SetDictionary( newMap ); - } - else - { - outValue->SetNull(); - } - - return true; -} - -static bool V8ValuesToCefList( CefRefPtr& outList, const CefV8ValueList& inList ) -{ - outList->SetSize( inList.size() ); - - size_t index = 0; - for ( const auto& x : inList ) - { - auto newValue = CefValue::Create(); - - if ( !V8ValueToCefValue( newValue, x ) ) - return false; - - outList->SetValue( index, newValue ); - index++; - } - - return true; -} - -static bool CefValueToV8Value( CefRefPtr& outValue, const CefRefPtr& inValue, int depth = 0 ) -{ - if ( depth > 16 ) - return false; - - switch ( inValue->GetType() ) - { - case VTYPE_NULL: - outValue = CefV8Value::CreateUndefined(); - break; - case VTYPE_BOOL: - outValue = CefV8Value::CreateBool( inValue->GetBool() ); - break; - case VTYPE_DOUBLE: - outValue = CefV8Value::CreateDouble( inValue->GetDouble() ); - break; - case VTYPE_STRING: - outValue = CefV8Value::CreateString( inValue->GetString() ); - break; - case VTYPE_LIST: - { - auto inList = inValue->GetList(); - outValue = CefV8Value::CreateArray( inList->GetSize() ); - - for ( size_t i = 0; i < inList->GetSize(); i++ ) - { - CefRefPtr entry; - - if ( !CefValueToV8Value( entry, inList->GetValue( i ), depth + 1 ) ) - return false; - - outValue->SetValue( i, entry ); - } - break; - } - case VTYPE_DICTIONARY: - { - auto inMap = inValue->GetDictionary(); - outValue = CefV8Value::CreateObject( nullptr, nullptr ); - - CefDictionaryValue::KeyList keys; - inMap->GetKeys( keys ); - - for ( const auto& key : keys ) - { - CefRefPtr value; - - if ( !CefValueToV8Value( value, inMap->GetValue( key ), depth + 1 ) ) - return false; - - outValue->SetValue( key, value, V8_PROPERTY_ATTRIBUTE_NONE ); - } - break; - } - default: - outValue = CefV8Value::CreateUndefined(); - break; - } - - return true; -} - -static bool CefListToV8Values( CefV8ValueList& outList, const CefRefPtr inList ) -{ - outList.reserve( inList->GetSize() ); - - for ( size_t i = 0; i < inList->GetSize(); i++ ) - { - CefRefPtr entry; - - if ( !CefValueToV8Value( entry, inList->GetValue( i ) ) ) - return false; - - outList.emplace_back( entry ); - } - - return true; -} - -// -// CefApp interface -// -void ChromiumApp::OnBeforeCommandLineProcessing( const CefString& process_type, CefRefPtr command_line ) -{ - command_line->AppendSwitch( "disable-gpu" ); - command_line->AppendSwitch( "disable-gpu-compositing" ); - command_line->AppendSwitch( "disable-smooth-scrolling" ); -#ifdef _WIN32 - command_line->AppendSwitch( "enable-begin-frame-scheduling" ); -#endif - command_line->AppendSwitch( "enable-system-flash" ); - - // This can interfere with posix signals and break Breakpad -#ifdef POSIX - command_line->AppendSwitch( "disable-in-process-stack-traces" ); -#endif - -#ifdef OSX - command_line->AppendSwitch( "use-mock-keychain" ); -#endif - - // https://bitbucket.org/chromiumembedded/cef/issues/2400 - command_line->AppendSwitchWithValue( "disable-features", "TouchpadAndWheelScrollLatching,AsyncWheelEvents" ); - - // Auto-play media - command_line->AppendSwitchWithValue( "autoplay-policy", "no-user-gesture-required" ); -} - -void ChromiumApp::OnRegisterCustomSchemes( CefRawPtr registrar ) -{ - // TODO: are these bools what we want them to be? - registrar->AddCustomScheme( "asset", CEF_SCHEME_OPTION_STANDARD | CEF_SCHEME_OPTION_CSP_BYPASSING ); -} - -CefRefPtr ChromiumApp::GetRenderProcessHandler() -{ - return this; -} - -// -// CefRenderProcessHandler interface -// -void ChromiumApp::OnContextCreated( CefRefPtr browser, CefRefPtr frame, CefRefPtr context ) -{ - // - // CEF3 doesn't support implementing the print dialog, so we've gotta just remove window.print. - // - context->Enter(); - { - context->GetGlobal()->DeleteValue( "print" ); - - // Removing WebSQL for now - we can add it back after CEF3 has been updated - context->GetGlobal()->DeleteValue( "openDatabase" ); - - } - context->Exit(); - - // If this is a web worker, we want nothing to do with it - if ( !browser ) - return; - - for ( auto& pair : m_RegisteredFunctions ) - { - RegisterFunctionInFrame( frame, pair.first, pair.second ); - } -} - -void ChromiumApp::OnContextReleased( CefRefPtr browser, CefRefPtr frame, CefRefPtr context ) -{ - -} - -bool ChromiumApp::OnProcessMessageReceived( CefRefPtr browser, CefRefPtr frame, CefProcessId source_process, CefRefPtr message ) -{ - auto name = message->GetName(); - auto args = message->GetArgumentList(); - - if ( name == "RegisterFunction" ) - { - RegisterFunction( browser, args ); - return true; - } - - if ( name == "ExecuteCallback" ) - { - ExecuteCallback( browser, args ); - return true; - } - - if ( name == "ForgetCallback" ) - { - ForgetCallback( browser, args ); - return true; - } - - if ( name == "ExecuteJavaScript" ) - { - ExecuteJavaScript( browser, args ); - return true; - } - - return false; -} - -// -// CefV8Handler interface -// -bool ChromiumApp::Execute( const CefString& name, CefRefPtr object, const CefV8ValueList& arguments, CefRefPtr& retval, CefString& exception ) -{ - auto context = CefV8Context::GetCurrentContext(); - auto browser = context->GetBrowser(); - - if ( !browser ) - { - // This could happen inside of a web worker (but our functions shouldn't be registered there) - exception = "CefV8Context::GetBrowser == nullptr"; - return true; - } - - // - // We need to rip the objName/funcName out of this function's name - // - std::string objName, funcName; - { - std::string fullName = name.ToString(); - size_t delimPos = fullName.find( '#' ); - - if ( delimPos == std::string::npos ) - { - // Shouldn't happen - exception = "ChromiumApp::Execute couldn't parse this function's name"; - return true; - } - - objName = fullName.substr( 0, delimPos ); - funcName = fullName.substr( delimPos + 1 ); - } - - auto argsList = CefListValue::Create(); - if ( !V8ValuesToCefList( argsList, arguments ) ) - { - exception = "failed to serialize parameters (did you return a deeply nested structure?)"; - return true; - } - - CefRefPtr callback; - - auto message = CefProcessMessage::Create( "ExecuteFunction" ); - auto args = message->GetArgumentList(); - - args->SetString( 0, objName ); - args->SetString( 1, funcName ); - - // No callback = easy mode - if ( arguments.empty() || !arguments.back()->IsFunction() ) - { - args->SetInt( 2, -1 ); // Invalid callback id - args->SetList( 3, argsList ); - } - else - { - // Now register a callback index that Lua will send back to us later - int callbackId = CreateCallback( context, arguments.back() ); - - // We pass argsList to Lua, so pop the callback off of it - argsList->Remove( argsList->GetSize() - 1 ); - - args->SetInt( 2, callbackId ); - args->SetList( 3, argsList ); - } - - browser->GetMainFrame()->SendProcessMessage( PID_BROWSER, message ); - return true; -} - -// - -int ChromiumApp::CreateCallback( CefRefPtr context, CefRefPtr func ) -{ - if ( m_Callbacks.find( m_NextCallbackId ) != m_Callbacks.end() ) - { - // We're overlapping? Probably shouldn't happen - return -1; - } - - int callbackId = m_NextCallbackId; - Callback cb; - cb.Context = context; - cb.Function = func; - - m_Callbacks.emplace( callbackId, cb ); - - m_NextCallbackId++; - if ( m_NextCallbackId >= 16384 ) - m_NextCallbackId = 0; - - return callbackId; -} - -void ChromiumApp::RegisterFunctionInFrame( CefRefPtr frame, const CefString& objName, const CefString& funcName ) -{ - if ( !frame ) - return; - - auto context = frame->GetV8Context(); - - if ( !context ) - return; - - // - // We can only associate one string with a function in CEF. So we'll use "{objName}#{funcName}". - // - CefString fullName; - { - std::stringstream ss; - ss << objName.ToString(); - ss << '#'; - ss << funcName.ToString(); - fullName = CefString( ss.str() ); - } - - context->Enter(); - { - auto window = context->GetGlobal(); - auto obj = window->GetValue( objName ); - - // If our object doesn't exist, create it - if ( !obj || !obj->IsObject() ) - { - obj = CefV8Value::CreateObject( nullptr, nullptr ); - window->SetValue( objName, obj, V8_PROPERTY_ATTRIBUTE_NONE ); - } - - auto func = CefV8Value::CreateFunction( fullName, this ); - obj->SetValue( funcName, func, V8_PROPERTY_ATTRIBUTE_NONE ); - } - context->Exit(); -} - -void ChromiumApp::ExecuteJavaScript( CefRefPtr browser, CefRefPtr args ) -{ - CefString jsUrl = args->GetString( 0 ); - CefString jsSrc = args->GetString( 1 ); - - auto mainFrame = browser->GetMainFrame(); - - if ( !mainFrame || !mainFrame->IsValid() ) - return; - - auto context = mainFrame->GetV8Context(); - - if ( !context || !context->IsValid() ) - return; - - context->Enter(); - { - CefRefPtr retVal; - CefRefPtr retException; - context->Eval( jsSrc, jsUrl, 1, retVal, retException ); - } - context->Exit(); -} - -void ChromiumApp::RegisterFunction( CefRefPtr browser, CefRefPtr args ) -{ - CefString objName = args->GetString( 0 ); - CefString funcName = args->GetString( 1 ); - - // Register this function in any frames that already exist - { - std::vector frames; - browser->GetFrameIdentifiers( frames ); - - for ( auto frameId : frames ) - { - RegisterFunctionInFrame( browser->GetFrame( frameId ), objName, funcName ); - } - } - - m_RegisteredFunctions.emplace_back( std::make_pair( objName, funcName ) ); -} - -void ChromiumApp::ExecuteCallback( CefRefPtr browser, CefRefPtr args ) -{ - int callbackId = args->GetInt( 0 ); - auto it = m_Callbacks.find( callbackId ); - if ( it == m_Callbacks.end() ) - return; - - auto context = it->second.Context; - auto func = it->second.Function; - - m_Callbacks.erase( it ); - - // Context has been destroyed for some reason - if ( !context->IsValid() ) - return; - - auto argList = args->GetList( 1 ); - - context->Enter(); - { - CefV8ValueList arguments; - if ( CefListToV8Values( arguments, argList ) ) - { - func->ExecuteFunction( nullptr, arguments ); - } - } - context->Exit(); -} - -void ChromiumApp::ForgetCallback( CefRefPtr browser, CefRefPtr args ) -{ - m_Callbacks.erase( args->GetInt( 0 ) ); -} \ No newline at end of file diff --git a/html_chromium/chromium_process/ChromiumApp.h b/html_chromium/chromium_process/ChromiumApp.h deleted file mode 100644 index d50e18d..0000000 --- a/html_chromium/chromium_process/ChromiumApp.h +++ /dev/null @@ -1,56 +0,0 @@ -#include "include/cef_app.h" - -#include -#include - -class ChromiumApp - : public CefApp - , public CefRenderProcessHandler - , public CefV8Handler -{ -public: - // - // CefApp interface - // - void OnBeforeCommandLineProcessing( const CefString& process_type, CefRefPtr command_line ) override; - void OnRegisterCustomSchemes( CefRawPtr registrar ) override; - CefRefPtr GetRenderProcessHandler() override; - - // - // CefRenderProcessHandler interface - // - void OnContextCreated( CefRefPtr browser, CefRefPtr frame, CefRefPtr context ) override; - void OnContextReleased( CefRefPtr browser, CefRefPtr frame, CefRefPtr context ) override; - bool OnProcessMessageReceived( CefRefPtr browser, CefRefPtr frame, CefProcessId source_process, CefRefPtr message ) override; - - // - // CefV8Handler interface - // - bool Execute( const CefString& name, CefRefPtr object, const CefV8ValueList& arguments, CefRefPtr& retval, CefString& exception ) override; - -private: - int CreateCallback( CefRefPtr context, CefRefPtr func ); - void RegisterFunctionInFrame( CefRefPtr frame, const CefString& objName, const CefString& funcName ); - - // Messages from the game process - void ExecuteJavaScript( CefRefPtr browser, CefRefPtr args ); - void RegisterFunction( CefRefPtr browser, CefRefPtr args ); - void ExecuteCallback( CefRefPtr browser, CefRefPtr args ); - void ForgetCallback( CefRefPtr browser, CefRefPtr args ); - -private: - std::vector> m_RegisteredFunctions; - - struct Callback - { - CefRefPtr Context; - CefRefPtr Function; - }; - - std::unordered_map m_Callbacks; - int m_NextCallbackId = 0; - -private: - IMPLEMENT_REFCOUNTING( ChromiumApp ); - -}; diff --git a/html_chromium/chromium_process/Linux.cpp b/html_chromium/chromium_process/Linux.cpp deleted file mode 100644 index 5667783..0000000 --- a/html_chromium/chromium_process/Linux.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "include/cef_app.h" -#include "ChromiumApp.h" - -int main( int argc, char* argv[] ) -{ - CefRefPtr chromiumApp( new ChromiumApp ); - - // 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. - return CefExecuteProcess( main_args, chromiumApp, nullptr ); -} diff --git a/html_chromium/chromium_process/Windows.cpp b/html_chromium/chromium_process/Windows.cpp deleted file mode 100644 index 0a44978..0000000 --- a/html_chromium/chromium_process/Windows.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include "include/cef_app.h" -#include "include/cef_sandbox_win.h" - -#include "ChromiumApp.h" - -int ChromiumMain( HINSTANCE hInstance ) -{ - CefRefPtr chromiumApp( new ChromiumApp ); - - // Provide CEF with command-line arguments. - CefMainArgs main_args( hInstance ); - -#if defined( _WIN32 ) && defined( NDEBUG ) - CefScopedSandboxInfo info; - return CefExecuteProcess( main_args, chromiumApp, info.sandbox_info() ); -#else - return CefExecuteProcess( main_args, chromiumApp, nullptr ); -#endif -} diff --git a/html_chromium/chromium_process/chromium_process.vpc b/html_chromium/chromium_process/chromium_process.vpc deleted file mode 100644 index f7c4db2..0000000 --- a/html_chromium/chromium_process/chromium_process.vpc +++ /dev/null @@ -1,45 +0,0 @@ -$Macro SRCDIR "..\..\.." - -// GMOD: GO - We have to append the PLATSUBDIR manually here because EXEs are unique. -$Include "$SRCDIR\vpc_scripts\platform_dirs.vpc" -$Macro OUTBINDIR "$SRCDIR\..\game\bin" [$WIN32] -$Macro OUTBINDIR "$SRCDIR\..\game\bin\$PLATSUBDIR" [!$WIN32 && !$OSX64] - -// We move this later -$Macro OUTBINDIR "$SRCDIR\..\build_scripts\osx64" [$OSX64] - -$Macro OUTBINNAME "chromium_process" - -$Include "$SRCDIR\vpc_scripts\source_exe_base.vpc" - -$Include "$SRCDIR\tier0\tier0_exclude.vpc" -$Include "$SRCDIR\tier1\tier1_exclude.vpc" -$Include "$SRCDIR\vstdlib\vstdlib_exclude.vpc" - -$Configuration -{ - $Compiler - { - $AdditionalIncludeDirectories "$BASE;$SRCDIR\thirdparty\linux\CEF" [$LINUX32] - $AdditionalIncludeDirectories "$BASE;$SRCDIR\thirdparty\linux64\CEF" [$LINUX64] - $AdditionalIncludeDirectories "$BASE;$SRCDIR\thirdparty\osx64\CEF" [$OSX64] - } - -} - -$Project "chromium_process" -{ - $Folder "Source Files" - { - $File "ChromiumApp.cpp" - $File "Linux.cpp" [$LINUX] - $File "macOS.cpp" [$OSX64] - } - - $Folder "Link Libraries" - { - $ImpLibExternal "$LIBCOMMON\cef" [$LINUX] - $LibExternal "$LIBCOMMON\libcef_dll_wrapper" [$LINUX || $OSX64] - $LibExternal "$LIBCOMMON\cef_sandbox" [$OSX64] - } -} diff --git a/html_chromium/chromium_process/macOS.cpp b/html_chromium/chromium_process/macOS.cpp deleted file mode 100644 index 44ef408..0000000 --- a/html_chromium/chromium_process/macOS.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "include/cef_app.h" -#include "include/wrapper/cef_library_loader.h" -#include "include/cef_sandbox_mac.h" -#include "ChromiumApp.h" - -// Entry point function for sub-processes. -int main( int argc, char* argv[] ) { - // HACK - - // Initialize the macOS sandbox for this helper process. - CefScopedSandboxContext sandbox_context; - if ( !sandbox_context.Initialize( argc, argv ) ) - return 1; - - // Load the CEF framework library at runtime instead of linking directly - // as required by the macOS sandbox implementation. - CefScopedLibraryLoader library_loader; - if ( !library_loader.LoadInHelper() ) - return 1; - - CefRefPtr chromiumApp( new ChromiumApp ); - - // Provide CEF with command-line arguments. - CefMainArgs main_args( argc, argv ); - - // Execute the sub-process. - return CefExecuteProcess( main_args, chromiumApp, nullptr ); -} diff --git a/html_chromium/html_chromium.vpc b/html_chromium/html_chromium.vpc deleted file mode 100644 index f1e2cb5..0000000 --- a/html_chromium/html_chromium.vpc +++ /dev/null @@ -1,74 +0,0 @@ -$Macro SRCDIR "..\.." -$Macro OUTBINDIR "$SRCDIR\..\game\bin" -$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc" - -// $Include "$SRCDIR\tier0\tier0_exclude.vpc" -// $Include "$SRCDIR\tier1\tier1_exclude.vpc" -// $Include "$SRCDIR\vstdlib\vstdlib_exclude.vpc" - -$Configuration -{ - $General - { - // Win32: Don't target XP for Chromium - $PlatformToolset "v142" [$CHROMIUM && $WIN32] - } - - $Compiler - { - $AdditionalIncludeDirectories "$BASE;$SRCDIR\garrysmod" - $AdditionalIncludeDirectories "$BASE;$SRCDIR\thirdparty\win32\CEF" [$WIN32] - $AdditionalIncludeDirectories "$BASE;$SRCDIR\thirdparty\win64\CEF" [$WIN64] - $AdditionalIncludeDirectories "$BASE;$SRCDIR\thirdparty\linux64\CEF" [$LINUX64] - $AdditionalIncludeDirectories "$BASE;$SRCDIR\thirdparty\osx64\CEF" [$OSX64] - - // Win32: Don't target XP for Chromium - $PreprocessorDefinitions "$BASE;WINVER=0x0601;_WIN32_WINNT=0x0601;NTDDI_VERSION=0x06010000" [$WIN32] - } - -} - -$Project "html_chromium" -{ - $Folder "Interfaces" - { - $File "$SRCDIR\garrysmod\html\IHtmlSystem.h" - $File "$SRCDIR\garrysmod\html\IHtmlClient.h" - $File "$SRCDIR\garrysmod\html\IHtmlClientListener.h" - $File "$SRCDIR\garrysmod\html\IHtmlResourceHandler.h" - $File "$SRCDIR\garrysmod\html\JSValue.h" - } - - $Folder "Source Files" - { - $File "ChromiumSystem.cpp" - $File "ChromiumClient.cpp" - $File "ChromiumBrowser.cpp" - $File "ResourceHandler.cpp" - $File "HtmlResourceHandler.cpp" - $File "JSObjects.cpp" - } - - $Folder "Header Files" - { - $File "cef_start.h" - $File "cef_end.h" - $File "ChromiumSystem.h" - $File "ChromiumClient.h" - $File "ChromiumBrowser.h" - $File "ResourceHandler.h" - $File "HtmlResourceHandler.h" - $File "MessageQueue.h" - $File "ImageData.h" - $File "JSObjects.h" - } - - $Folder "Link Libraries" - { - $LibExternal "$LIBCOMMON\libcef_dll_wrapper" - - // macOS doesn't link against libcef at compile-time - $ImpLibExternal "$LIBCOMMON\libcef" [$WINDOWS] - $ImpLibExternal "$LIBCOMMON\cef" [$LINUX] - } -} diff --git a/html_stub/CMakeLists.txt b/html_stub/CMakeLists.txt index ff932e0..0ac031e 100644 --- a/html_stub/CMakeLists.txt +++ b/html_stub/CMakeLists.txt @@ -1,3 +1,4 @@ + set(SOURCES StubClient.cpp StubClient.h @@ -5,5 +6,22 @@ set(SOURCES StubSystem.h) add_library(html_stub SHARED ${SOURCES}) +add_dependencies(html_stub html) target_link_libraries(html_stub html) -SET_TARGET_PROPERTIES(html_stub PROPERTIES PREFIX "") + +set_target_properties(html_stub PROPERTIES PREFIX "") +if(OS_LINUX) + set_target_properties(html_stub PROPERTIES OUTPUT_NAME "html_stub_client") +endif() + +if(OS_WINDOWS) + if(CMAKE_SIZEOF_VOID_P MATCHES 8) + install(TARGETS html_stub DESTINATION "${INSTALL_OUT_DIR}/GarrysMod/bin/win64") + else() + install(TARGETS html_stub DESTINATION "${INSTALL_OUT_DIR}/GarrysMod/bin") + endif() +elseif(OS_LINUX) + install(TARGETS html_stub DESTINATION "${INSTALL_OUT_DIR}/GarrysMod/bin/${CEF_PLATFORM}") +elseif(OS_MAC) + install(TARGETS html_stub DESTINATION "${INSTALL_OUT_DIR}/GarrysMod_Signed.app/Contents/MacOS") +endif() diff --git a/html_stub/html_stub.vpc b/html_stub/html_stub.vpc deleted file mode 100644 index 6df12f6..0000000 --- a/html_stub/html_stub.vpc +++ /dev/null @@ -1,35 +0,0 @@ -$Macro SRCDIR "..\.." -$Macro OUTBINDIR "$SRCDIR\..\game\bin" -$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc" - -$Include "$SRCDIR\tier0\tier0_exclude.vpc" -$Include "$SRCDIR\tier1\tier1_exclude.vpc" -$Include "$SRCDIR\vstdlib\vstdlib_exclude.vpc" - -$Configuration -{ - $Compiler - { - $AdditionalIncludeDirectories "$BASE;$SRCDIR\garrysmod" - } -} - -$Project "html_stub" -{ - $Folder "Interfaces" - { - $File "$SRCDIR\garrysmod\html\IHtmlSystem.h" - $File "$SRCDIR\garrysmod\html\IHtmlClient.h" - $File "$SRCDIR\garrysmod\html\IHtmlClientListener.h" - $File "$SRCDIR\garrysmod\html\IHtmlResourceHandler.h" - $File "$SRCDIR\garrysmod\html\JSValue.h" - } - - $Folder "Source Files" - { - $File "StubSystem.cpp" - $File "StubSystem.h" - $File "StubClient.cpp" - $File "StubClient.h" - } -} diff --git a/thirdparty/glad/CMakeLists.txt b/thirdparty/glad/CMakeLists.txt index 47da0fe..3b95e59 100644 --- a/thirdparty/glad/CMakeLists.txt +++ b/thirdparty/glad/CMakeLists.txt @@ -1,8 +1,11 @@ + set(SOURCES src/blank.cpp src/glad.c include/glad/glad.h include/KHR/khrplatform.h) +set_source_files_properties(src/glad.c PROPERTIES LANGUAGE CXX) + add_library(glad STATIC ${SOURCES}) target_include_directories(glad PUBLIC include) diff --git a/thirdparty/imgui-1.74/CMakeLists.txt b/thirdparty/imgui-1.74/CMakeLists.txt index a3269f4..0cff301 100644 --- a/thirdparty/imgui-1.74/CMakeLists.txt +++ b/thirdparty/imgui-1.74/CMakeLists.txt @@ -1,3 +1,6 @@ + +project(imgui) + set(SOURCES imconfig.h imgui.cpp @@ -13,6 +16,8 @@ set(SOURCES imstb_textedit.h imstb_truetype.h) + add_library(imgui STATIC ${SOURCES}) -target_link_libraries(imgui glfw glad) +target_compile_definitions(imgui PRIVATE IMGUI_IMPL_OPENGL_LOADER_GLAD) +target_link_libraries(imgui glfw glad ${glfw_LIBRARIES}) target_include_directories(imgui PUBLIC ./)