├── .gitignore ├── demo-3d ├── nogui │ ├── CMakeLists.txt │ ├── nogui-3d-main.cpp │ ├── NoGUIApp.hpp │ └── NoGUIApp.cpp ├── CMakeLists.txt ├── sokol-demo │ ├── CMakeLists.txt │ └── demo │ │ ├── Demo.hpp │ │ ├── GUI.hpp │ │ ├── App.hpp │ │ ├── Demo.cpp │ │ └── App.cpp └── cef │ ├── URL.h │ ├── DemoCefApp.hpp │ ├── CMakeLists.txt │ ├── DemoCefRendererApp.hpp │ ├── DemoCefRendererApp.cpp │ ├── cef-3d-async-main.cpp │ └── DemoCefApp.cpp ├── third_party ├── jsbind-cef │ └── CMakeLists.txt ├── CMakeLists.txt └── boost │ └── CMakeLists.txt ├── ws-simple ├── CMakeLists.txt ├── README.md └── ws_simple_server.cpp ├── cef-sync ├── README.md ├── CMakeLists.txt └── cef-sync.cpp ├── fs-browser ├── browser-lib │ ├── CMakeLists.txt │ └── fsbrowser │ │ ├── Dir.hpp │ │ ├── FSBrowser.hpp │ │ ├── Dir.cpp │ │ ├── FSBrowser.cpp │ │ └── win_dirent.h ├── cli │ └── fs-cli.cpp ├── CMakeLists.txt ├── ws-server │ └── fs-ws-server.cpp └── cef │ └── fs-cef.cpp ├── .gitmodules ├── helper_lib ├── CMakeLists.txt └── helper │ ├── DirUtil.hpp │ └── DirUtil.cpp ├── cef-async ├── README.md ├── CMakeLists.txt └── cef-async.cpp ├── LICENSE ├── cmake └── src_group.cmake ├── CMakeLists.txt ├── html ├── cef-echo.html ├── ws-simple.html ├── fs-browser.html └── game-gui.html └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .vscode/ 3 | -------------------------------------------------------------------------------- /demo-3d/nogui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(nogui-3d 2 | nogui-3d-main.cpp 3 | NoGUIApp.cpp 4 | NoGUIApp.hpp 5 | ) 6 | 7 | target_link_libraries(nogui-3d sokol-demo) -------------------------------------------------------------------------------- /demo-3d/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Lib 2 | add_subdirectory(sokol-demo) 3 | 4 | set_target_properties(sokol-demo PROPERTIES FOLDER "demos\\demo-3d") 5 | 6 | add_subdirectory(nogui) 7 | add_subdirectory(cef) 8 | -------------------------------------------------------------------------------- /third_party/jsbind-cef/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(JSBIND_CEF TRUE) 2 | set(JSBIND_TARGET_NAME jsbind-cef) 3 | set(JSBIND_JS_BACKEND_LIBS cefdll_wrapper) 4 | include(../jsbind/code/jsbind.cmake) 5 | set_target_properties(jsbind-cef PROPERTIES FOLDER "third_party") 6 | -------------------------------------------------------------------------------- /ws-simple/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(ws-simple-server 2 | ws_simple_server.cpp 3 | ) 4 | 5 | target_link_libraries(ws-simple-server 6 | boost-beast 7 | ${CMAKE_THREAD_LIBS_INIT} 8 | ) 9 | 10 | set_target_properties(ws-simple-server PROPERTIES FOLDER "demos") 11 | -------------------------------------------------------------------------------- /demo-3d/sokol-demo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(sokol-demo STATIC 2 | demo/Demo.hpp 3 | demo/Demo.cpp 4 | demo/App.hpp 5 | demo/App.cpp 6 | demo/GUI.hpp 7 | ) 8 | 9 | target_include_directories(sokol-demo INTERFACE .) 10 | 11 | target_link_libraries(sokol-demo PUBLIC 12 | sokol 13 | ) -------------------------------------------------------------------------------- /cef-sync/README.md: -------------------------------------------------------------------------------- 1 | # cef-sync 2 | 3 | A simple [CEF](https://bitbucket.org/chromiumembedded/cef-project/src/master/)-based echo application 4 | 5 | * Uses synchrnonous communication with JS bindings based on the [jsbind](https://github.com/Chobolabs/jsbind/) library 6 | * The entire code runs in the Chromium renderer process 7 | -------------------------------------------------------------------------------- /fs-browser/browser-lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(fsbrowser STATIC 2 | fsbrowser/Dir.cpp 3 | fsbrowser/Dir.hpp 4 | fsbrowser/FSBrowser.hpp 5 | fsbrowser/FSBrowser.cpp 6 | ) 7 | 8 | target_include_directories(fsbrowser INTERFACE .) 9 | 10 | target_link_libraries(fsbrowser PUBLIC 11 | helper_lib 12 | ) -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/cef-cmake"] 2 | path = third_party/cef-cmake 3 | url = https://github.com/iboB/cef-cmake.git 4 | [submodule "third_party/jsbind"] 5 | path = third_party/jsbind 6 | url = https://github.com/Chobolabs/jsbind.git 7 | [submodule "third_party/sokol"] 8 | path = third_party/sokol 9 | url = https://github.com/floooh/sokol.git 10 | -------------------------------------------------------------------------------- /demo-3d/cef/URL.h: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #pragma once 9 | 10 | #define URI_ROOT "http://htmldemos" 11 | static const char* const URL = URI_ROOT "/game-gui.html"; 12 | -------------------------------------------------------------------------------- /helper_lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(srcs) 2 | src_group(platform srcs 3 | helper/DirUtil.hpp 4 | helper/DirUtil.cpp 5 | ) 6 | 7 | add_library(helper_lib STATIC 8 | ${srcs} 9 | ) 10 | 11 | target_include_directories(helper_lib PUBLIC .) 12 | 13 | target_link_libraries(helper_lib PUBLIC 14 | ${CMAKE_DL_LIBS} 15 | ) 16 | 17 | set_target_properties(helper_lib PROPERTIES FOLDER "helper") 18 | -------------------------------------------------------------------------------- /demo-3d/cef/DemoCefApp.hpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #pragma once 9 | 10 | #include 11 | 12 | class DemoCefApp : public demo::App 13 | { 14 | virtual demo::GUI* createGUI() override; 15 | }; -------------------------------------------------------------------------------- /demo-3d/nogui/nogui-3d-main.cpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #include 9 | #include "NoGUIApp.hpp" 10 | 11 | int main() 12 | { 13 | return demo::Demo::run(std::make_unique()); 14 | } -------------------------------------------------------------------------------- /demo-3d/nogui/NoGUIApp.hpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #pragma once 9 | 10 | #include 11 | 12 | class NoGUIApp : public demo::App 13 | { 14 | virtual demo::GUI* createGUI() override; 15 | }; 16 | -------------------------------------------------------------------------------- /cef-async/README.md: -------------------------------------------------------------------------------- 1 | # cef-async 2 | 3 | A simple [CEF](https://bitbucket.org/chromiumembedded/cef-project/src/master/)-based echo application 4 | 5 | * The render process communicates with JS bindings based on the [jsbind](https://github.com/Chobolabs/jsbind/) library 6 | * It sends a process message to the browser process with the input from clients 7 | * The browser process send process messages back to the render process for a response 8 | -------------------------------------------------------------------------------- /ws-simple/README.md: -------------------------------------------------------------------------------- 1 | # ws-simple 2 | 3 | A simple WebSocket echo server and client. 4 | 5 | * The C++ server uses [Boost.Beast](https://github.com/boostorg/beast) 6 | * The HTML 5 client doesn't have any dependencies 7 | 8 | Note that the server is WebSockets only. You will need an additional simple HTTP server to provide the HTML static content. Any HTTP server will do. For example one of the simplest ones is `python -m SimpleHTTPServer`. 9 | -------------------------------------------------------------------------------- /fs-browser/cli/fs-cli.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "fsbrowser/FSBrowser.hpp" 3 | 4 | int main() 5 | { 6 | fsbrowser::FSBrowser fsBrowser; 7 | 8 | std::string curRequest = "contents:"; 9 | while(true) 10 | { 11 | std::string response = fsBrowser.onRequest(curRequest); 12 | std::cout << response << std::endl; 13 | std::getline(std::cin, curRequest); 14 | if (curRequest == "quit") break; 15 | } 16 | 17 | return 0; 18 | } -------------------------------------------------------------------------------- /demo-3d/sokol-demo/demo/Demo.hpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #pragma once 9 | 10 | #include 11 | 12 | namespace demo 13 | { 14 | 15 | class App; 16 | 17 | class Demo 18 | { 19 | public: 20 | // init and run sokol 21 | static int run(std::unique_ptr app); 22 | }; 23 | 24 | } -------------------------------------------------------------------------------- /cef-async/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(cef-async 2 | cef-async.cpp 3 | ${CEF_CMAKE_EXECUTABLE_RESOURCES} 4 | ) 5 | 6 | target_link_libraries(cef-async 7 | cefdll_wrapper 8 | jsbind-cef 9 | helper_lib 10 | ) 11 | 12 | # this should be added by cef-cmake 13 | # but to support earlier cmake versions which don't support 14 | # target_link_options we manually add them here 15 | if (CEF_CMAKE_OS_POSIX) 16 | set_target_properties(cef-async PROPERTIES 17 | # passing local rpath linker flags 18 | LINK_FLAGS "-Wl,-rpath,.:$ORIGIN" 19 | ) 20 | endif() 21 | 22 | set_target_properties(cef-async PROPERTIES FOLDER "demos") 23 | -------------------------------------------------------------------------------- /cef-sync/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(cef-sync 2 | cef-sync.cpp 3 | ${CEF_CMAKE_EXECUTABLE_RESOURCES} 4 | ) 5 | 6 | target_link_libraries(cef-sync 7 | cefdll_wrapper 8 | jsbind-cef 9 | helper_lib 10 | ) 11 | 12 | # this should be added by cef-cmake 13 | # but to support earlier cmake versions which don't support 14 | # target_link_options we manually add them here 15 | if (CEF_CMAKE_OS_POSIX) 16 | set_target_properties(cef-sync PROPERTIES 17 | # passing local rpath linker flags 18 | LINK_FLAGS "-Wl,-rpath,.:$ORIGIN" 19 | ) 20 | endif() 21 | 22 | set_target_properties(cef-sync PROPERTIES FOLDER "demos") 23 | 24 | -------------------------------------------------------------------------------- /third_party/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(boost) 2 | 3 | if(NOT HTML_5_DEMO_CLI_ONLY) 4 | add_subdirectory(cef-cmake) 5 | add_subdirectory(jsbind-cef) 6 | 7 | add_library(sokol INTERFACE) 8 | target_sources(sokol INTERFACE 9 | ${CMAKE_CURRENT_SOURCE_DIR}/sokol/sokol_app.h 10 | ${CMAKE_CURRENT_SOURCE_DIR}/sokol/sokol_gfx.h 11 | ) 12 | target_include_directories(sokol INTERFACE sokol) 13 | if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") 14 | target_link_libraries(sokol INTERFACE 15 | GL 16 | X11 17 | dl 18 | pthread 19 | ) 20 | endif() 21 | endif() 22 | -------------------------------------------------------------------------------- /third_party/boost/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(NOT HTML5_DEMO_BOOST_DIR) 2 | if (OS_WINDOWS) 3 | set(HTML5_DEMO_BOOST_DIR boost-win) 4 | else() 5 | set(HTML5_DEMO_BOOST_DIR boost-unix) 6 | endif() 7 | endif() 8 | 9 | # "fake" target which only gives us the boost headers as an include directory 10 | add_library(boost-headers INTERFACE) 11 | target_include_directories(boost-headers INTERFACE ${HTML5_DEMO_BOOST_DIR}) 12 | target_compile_definitions(boost-headers INTERFACE 13 | -D_SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING=1 14 | -DBOOST_ALL_NO_LIB=1 15 | ) 16 | 17 | add_library(boost-beast INTERFACE) 18 | target_link_libraries(boost-beast INTERFACE boost-headers) 19 | -------------------------------------------------------------------------------- /fs-browser/browser-lib/fsbrowser/Dir.hpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #pragma once 9 | 10 | #include 11 | #include 12 | 13 | namespace fsbrowser 14 | { 15 | 16 | class Dir 17 | { 18 | public: 19 | Dir(const std::string& basePath); 20 | ~Dir(); 21 | 22 | struct DirectoryContents 23 | { 24 | std::vector files; 25 | std::vector dirs; 26 | }; 27 | 28 | // path will be interpreted as relative to base path 29 | DirectoryContents getDirectoryContents(const char* path) const; 30 | 31 | private: 32 | std::string m_basePath; 33 | }; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /demo-3d/cef/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(cef-3d-async 2 | cef-3d-async-main.cpp 3 | DemoCefApp.hpp 4 | DemoCefApp.cpp 5 | DemoCefRendererApp.cpp 6 | DemoCefRendererApp.hpp 7 | URL.h 8 | ${CEF_CMAKE_EXECUTABLE_RESOURCES} 9 | ) 10 | 11 | target_link_libraries(cef-3d-async 12 | sokol-demo 13 | cefdll_wrapper 14 | jsbind-cef 15 | helper_lib 16 | ) 17 | 18 | # this should be added by cef-cmake 19 | # but to support earlier cmake versions which don't support 20 | # target_link_options we manually add them here 21 | if (CEF_CMAKE_OS_POSIX) 22 | set_target_properties(cef-3d-async PROPERTIES 23 | # passing local rpath linker flags 24 | LINK_FLAGS "-Wl,-rpath,.:$ORIGIN" 25 | ) 26 | endif() 27 | 28 | set_target_properties(cef-3d-async PROPERTIES FOLDER "demos\\demo-3d") 29 | -------------------------------------------------------------------------------- /helper_lib/helper/DirUtil.hpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #pragma once 9 | 10 | #include 11 | 12 | class DirUtil 13 | { 14 | public: 15 | // get the full path to the current executable 16 | static std::string getCurrentExecutablePath(); 17 | 18 | // starts looking from the current directory upwards until it discovers a valid subdirectory described by assetDir 19 | // for example 20 | // getAssetPath("/home/someuser/projects/xxx/build/bin", "assets"); will return /home/someuser/projects/xxx/assets if this directory exists 21 | static std::string getAssetPath(std::string baseDir, const std::string& assetDir); 22 | }; 23 | -------------------------------------------------------------------------------- /fs-browser/browser-lib/fsbrowser/FSBrowser.hpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #pragma once 9 | 10 | #include 11 | #include 12 | 13 | namespace fsbrowser 14 | { 15 | 16 | class Dir; 17 | 18 | class FSBrowser 19 | { 20 | public: 21 | FSBrowser(); 22 | ~FSBrowser(); 23 | 24 | // process a request according to the format in fs-browser.html 25 | // returns the response 26 | std::string onRequest(const std::string& request); 27 | 28 | // just a proxy to Dir::getDirectoryContents which converts the output to 29 | // a JSON string corresponding to the format accepted by fs-browser.html 30 | std::string dir(const std::string& path) const; 31 | 32 | private: 33 | std::unique_ptr m_dir; 34 | }; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /demo-3d/sokol-demo/demo/GUI.hpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #pragma once 9 | 10 | struct sapp_event; 11 | 12 | namespace demo 13 | { 14 | 15 | class GUI 16 | { 17 | public: 18 | virtual ~GUI() = default; 19 | 20 | virtual void update() = 0; 21 | virtual void draw() = 0; 22 | virtual void onEvent(const sapp_event& e) = 0; 23 | virtual void shutdown() = 0; 24 | 25 | // update revolutions with a pointer to three integers x;y;z 26 | virtual void updateRevolutions(const int* rev) = 0; 27 | 28 | enum RotationAxis 29 | { 30 | R_X, R_Y, R_Z, 31 | }; 32 | 33 | RotationAxis rotationAxis() const { return m_rotationAxis; } 34 | bool rotating() const { return m_rotating; } 35 | float rotationSpeed() const { return m_rotationSpeed; } 36 | 37 | protected: 38 | RotationAxis m_rotationAxis = R_X; 39 | float m_rotationSpeed = 1; // radians per second 40 | bool m_rotating = false; 41 | }; 42 | 43 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Borislav Stanimirov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /demo-3d/cef/DemoCefRendererApp.hpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | class DemoCefRendererApp : public CefApp, public CefRenderProcessHandler 15 | { 16 | public: 17 | DemoCefRendererApp(); 18 | ~DemoCefRendererApp(); 19 | 20 | CefRefPtr GetRenderProcessHandler() override { return this; } 21 | 22 | void OnContextCreated(CefRefPtr browser, 23 | CefRefPtr frame, CefRefPtr context) override; 24 | void OnContextReleased(CefRefPtr browser, 25 | CefRefPtr frame, CefRefPtr context) override; 26 | bool OnProcessMessageReceived(CefRefPtr browser, CefRefPtr frame, 27 | CefProcessId source_process, CefRefPtr message) override; 28 | private: 29 | IMPLEMENT_REFCOUNTING(DemoCefRendererApp); 30 | DISALLOW_COPY_AND_ASSIGN(DemoCefRendererApp); 31 | }; -------------------------------------------------------------------------------- /demo-3d/sokol-demo/demo/App.hpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #pragma once 9 | 10 | #include 11 | 12 | struct sapp_event; 13 | struct sapp_desc; 14 | 15 | namespace demo 16 | { 17 | 18 | class GUI; 19 | class Demo; 20 | 21 | class RotatingCube; 22 | 23 | struct vec3 24 | { 25 | float c[3]; 26 | }; 27 | 28 | struct int3 29 | { 30 | int c[3]; 31 | }; 32 | 33 | class App 34 | { 35 | public: 36 | App(); 37 | virtual ~App(); 38 | 39 | void init(); 40 | void mainLoop(); 41 | void shutdown(); 42 | void onEvent(const sapp_event& e); 43 | 44 | static uint32_t getTicks(); 45 | 46 | protected: 47 | virtual GUI* createGUI() = 0; 48 | 49 | private: 50 | friend class Demo; 51 | 52 | std::unique_ptr m_cube; 53 | 54 | GUI* m_gui = nullptr; 55 | 56 | vec3 m_rotation = {}; 57 | int3 m_revolutions = {}; 58 | 59 | // times and frames 60 | uint32_t m_currentFrameTime = 0; // start of current frame (ms) 61 | uint32_t m_timeSinceLastFrame = 0; // used as delta for updates 62 | }; 63 | 64 | } 65 | -------------------------------------------------------------------------------- /cmake/src_group.cmake: -------------------------------------------------------------------------------- 1 | # MaiBo 2 | # Copyright (c) 2015 Borislav Stanimirov 3 | # 4 | # Distributed under the MIT Software License 5 | # See accompanying file LICENSE.txt or copy at 6 | # http://opensource.org/licenses/MIT 7 | # 8 | # src_group 9 | # 10 | # Defines a group of source files, while also appending them to a list 11 | # (to be used with add_executable or add_library) 12 | # Args: 13 | # GROUP_NAME - name of the group. Subroups are separated by "~" 14 | # SRC_LIST - list of sources to append to 15 | # ... the other arguments are a list of files 16 | # 17 | # Example Usage 18 | # set(mySources) 19 | # src_group("Group~Subgroup" mySources file1 file2 ... fileN) 20 | # add_executable(myexe mySources) 21 | # 22 | macro(src_group GROUP_NAME SRC_LIST) 23 | # In order to work CMake's source_group macro requiers backslashes between 24 | # groups and subgroups. However this leads to escape hell. That's why we 25 | # use tildes and then replace them with backslashes at the very end 26 | set(DEMANGLED_GROUP) 27 | string(REPLACE "~" "\\\\" DEMANGLED_GROUP ${GROUP_NAME}) 28 | 29 | source_group(${DEMANGLED_GROUP} FILES ${ARGN}) 30 | 31 | foreach(filename ${ARGN}) 32 | list(APPEND ${SRC_LIST} ${filename}) 33 | endforeach() 34 | endmacro(src_group) 35 | -------------------------------------------------------------------------------- /fs-browser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Lib 2 | add_subdirectory(browser-lib) 3 | 4 | set_target_properties(fsbrowser PROPERTIES FOLDER "demos\\fs-browser") 5 | 6 | # CLI 7 | add_executable(fs-cli 8 | cli/fs-cli.cpp 9 | ) 10 | 11 | target_link_libraries(fs-cli fsbrowser) 12 | 13 | # WS 14 | add_executable(fs-ws-server 15 | ws-server/fs-ws-server.cpp 16 | ) 17 | 18 | target_link_libraries(fs-ws-server 19 | fsbrowser 20 | boost-beast 21 | ${CMAKE_THREAD_LIBS_INIT} 22 | ) 23 | 24 | set_target_properties(fs-ws-server PROPERTIES FOLDER "demos\\fs-browser") 25 | 26 | 27 | if(NOT HTML_5_DEMO_CLI_ONLY) 28 | # CEF 29 | add_executable(fs-cef 30 | cef/fs-cef.cpp 31 | ${CEF_CMAKE_EXECUTABLE_RESOURCES} 32 | ) 33 | 34 | target_link_libraries(fs-cef 35 | cefdll_wrapper 36 | jsbind-cef 37 | fsbrowser 38 | ) 39 | 40 | # this should be added by cef-cmake 41 | # but to support earlier cmake versions which don't support 42 | # target_link_options we manually add them here 43 | if (CEF_CMAKE_OS_POSIX) 44 | set_target_properties(fs-cef PROPERTIES 45 | # passing local rpath linker flags 46 | LINK_FLAGS "-Wl,-rpath,.:$ORIGIN" 47 | ) 48 | endif() 49 | 50 | set_target_properties(fs-cef PROPERTIES FOLDER "demos\\fs-browser") 51 | endif() -------------------------------------------------------------------------------- /demo-3d/sokol-demo/demo/Demo.cpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #include "Demo.hpp" 9 | 10 | #include "App.hpp" 11 | 12 | #define SOKOL_NO_DEPRECATED 13 | #define SOKOL_IMPL 14 | #define SOKOL_NO_ENTRY 15 | #define SOKOL_GLCORE33 16 | #if !defined(NDEBUG) 17 | # define SOKOL_DEBUG 1 18 | #endif 19 | #include 20 | #include 21 | 22 | namespace demo 23 | { 24 | 25 | namespace 26 | { 27 | App* g_app; 28 | 29 | void sinit() 30 | { 31 | g_app->init(); 32 | } 33 | 34 | void smainLoop() 35 | { 36 | g_app->mainLoop(); 37 | } 38 | 39 | void scleanup() 40 | { 41 | g_app->shutdown(); 42 | } 43 | 44 | void sonEvent(const sapp_event* e) 45 | { 46 | g_app->onEvent(*e); 47 | } 48 | } 49 | 50 | int Demo::run(std::unique_ptr app) 51 | { 52 | g_app = app.get(); 53 | 54 | sapp_desc desc = {}; 55 | desc.window_title = "cef-3d-demo"; 56 | desc.width = 1024; 57 | desc.height = 768; 58 | desc.init_cb = sinit; 59 | desc.frame_cb = smainLoop; 60 | desc.cleanup_cb = scleanup; 61 | desc.event_cb = sonEvent; 62 | 63 | int ret = sapp_run(&desc); 64 | g_app = nullptr; 65 | 66 | return ret; 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /demo-3d/nogui/NoGUIApp.cpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #include "NoGUIApp.hpp" 9 | 10 | #include 11 | 12 | #define SOKOL_NO_DEPRECATED 13 | #include 14 | 15 | namespace 16 | { 17 | class NoGUI : public demo::GUI 18 | { 19 | void update() override {} 20 | void draw() override {} 21 | void onEvent(const sapp_event& e) override 22 | { 23 | if (e.type == SAPP_EVENTTYPE_KEY_UP) 24 | { 25 | switch (e.key_code) 26 | { 27 | case SAPP_KEYCODE_Z: m_rotating = !m_rotating; break; 28 | case SAPP_KEYCODE_A: m_rotationSpeed += 0.3f; break; 29 | case SAPP_KEYCODE_S: m_rotationSpeed -= 0.3f; break; 30 | case SAPP_KEYCODE_Q: m_rotationAxis = R_X; break; 31 | case SAPP_KEYCODE_W: m_rotationAxis = R_Y; break; 32 | case SAPP_KEYCODE_E: m_rotationAxis = R_Z; break; 33 | default:; 34 | } 35 | } 36 | } 37 | void shutdown() override 38 | { 39 | delete this; 40 | } 41 | void updateRevolutions(const int* rev) override 42 | { 43 | printf("Revs: %d %d %d\n", rev[0], rev[1], rev[2]); 44 | } 45 | }; 46 | } 47 | 48 | demo::GUI* NoGUIApp::createGUI() 49 | { 50 | return new NoGUI; 51 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | set(CMAKE_CXX_STANDARD 17) 4 | set(CMAKE_CXX_EXTENSIONS OFF) 5 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 6 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 7 | 8 | project(html5-gui-demo) 9 | 10 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 11 | 12 | option(HTML_5_DEMO_CLI_ONLY "only enable command-line demos (suitable when there's no X)" OFF) 13 | 14 | ###################### 15 | # warnings 16 | if(MSVC) 17 | set(WG_WARNING_FLAGS "") # the default is fine here 18 | else() 19 | set(WG_WARNING_FLAGS "-Wall -Wextra -Wno-unused-variable") 20 | endif() 21 | 22 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WG_WARNING_FLAGS}") 23 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WG_WARNING_FLAGS}") 24 | 25 | ###################### 26 | # cmake lib 27 | set(CMAKE_MODULE_PATH 28 | ${CMAKE_MODULE_PATH} 29 | ${CMAKE_CURRENT_SOURCE_DIR}/cmake 30 | ${CMAKE_CURRENT_SOURCE_DIR}/third_party/cef-cmake/cmake 31 | ) 32 | include(src_group) 33 | 34 | ###################### 35 | # deps 36 | find_package(Threads REQUIRED) 37 | 38 | set(CEF_USE_SANDBOX OFF CACHE BOOL "Force turning off of sandbox") 39 | include(cef_cmake) 40 | 41 | ###################### 42 | # subdirectories 43 | add_subdirectory(third_party) 44 | if(NOT HTML_5_DEMO_CLI_ONLY) 45 | set_target_properties(cefdll_wrapper PROPERTIES FOLDER "third_party") 46 | endif() 47 | 48 | add_subdirectory(helper_lib) 49 | add_subdirectory(ws-simple) 50 | if(NOT HTML_5_DEMO_CLI_ONLY) 51 | add_subdirectory(cef-sync) 52 | add_subdirectory(cef-async) 53 | add_subdirectory(demo-3d) 54 | endif() 55 | add_subdirectory(fs-browser) -------------------------------------------------------------------------------- /html/cef-echo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 49 | 50 | 51 |

Echo

52 | 53 | 54 | 55 | 56 | 57 |
58 | 59 | 60 | -------------------------------------------------------------------------------- /fs-browser/browser-lib/fsbrowser/Dir.cpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #include "Dir.hpp" 9 | 10 | #if defined(_WIN32) 11 | # include 12 | # include "win_dirent.h" 13 | #else 14 | # include 15 | #endif 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | namespace fsbrowser 22 | { 23 | 24 | namespace 25 | { 26 | void normalizePath(std::string& path) 27 | { 28 | for (auto& c : path) 29 | if (c == '\\') 30 | c = '/'; 31 | } 32 | } 33 | 34 | Dir::Dir(const std::string& basePath) 35 | : m_basePath(basePath) 36 | {} 37 | 38 | Dir::~Dir() = default; 39 | 40 | Dir::DirectoryContents Dir::getDirectoryContents(const char* path) const 41 | { 42 | DirectoryContents ret; 43 | 44 | auto dir = m_basePath + path; 45 | auto dp = opendir(dir.c_str()); 46 | 47 | dirent* entry; 48 | while ((entry = readdir(dp)) != nullptr) 49 | { 50 | if (strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0) continue; // don't care for this 51 | auto fullPath = dir + "/" + entry->d_name; 52 | struct stat info; 53 | if (stat(fullPath.c_str(), &info) == 0 && (info.st_mode & S_IFDIR)) 54 | { 55 | ret.dirs.emplace_back(entry->d_name); 56 | } 57 | else 58 | { 59 | ret.files.emplace_back(entry->d_name); 60 | } 61 | } 62 | 63 | closedir(dp); 64 | 65 | return ret; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HTML 5 GUI Demo 2 | 3 | Demos for using HTML 5 as a GUI for C++ apps 4 | 5 | ## Talk 6 | 7 | The demos were created for a talk I gave at CppCon. 8 | 9 | * [Video of the talk](https://www.youtube.com/watch?v=bbbcZd4cuxg) 10 | * [Slides for the talk](http://ibob.github.io/slides/html5-gui/) 11 | 12 | ## Demos list 13 | 14 | * [ws-simple](ws-simple): A simple echo-server using WebSockets. Uses [Boost.Beast](https://github.com/boostorg/beast) 15 | * [cef-async](cef-async): A simple [CEF](https://bitbucket.org/chromiumembedded/cef-project/src/master/) echo application using *asynchronous* communication 16 | * [cef-sync](cef-sync): A simple [CEF](https://bitbucket.org/chromiumembedded/cef-project/src/master/) echo application using synchronous communication 17 | * [fs-browser](fs-browser): A simple filesystem browser demo with two implementations: WebSockets and CEF 18 | * [demo-3d](demo-3d): A simple demo with GUI over a custom OpenGL window with two implementations: No GUI and CEF 19 | 20 | ## Building 21 | 22 | *This repo has submodules*. Clone with `--recursive` or call `git submodule update --init` after cloning. 23 | 24 | There's a master CMakeLists.txt in the root directory, which can be used to build all demos. It downloads and prepares all dependencies, except Boost. You need to configure with `-DHTML5_DEMO_BOOST_DIR=path/to/your/boost/installation` or have Boost in your global include and library paths. 25 | 26 | Currently the only supported configuration is to build all demos. I may provide configuration options to build only selected ones, but it won't be soon. In the meantime I welcome pull requests which do so. 27 | 28 | ## License 29 | 30 | All demos under the MIT license. See accompanying file LICENSE or copy [here](https://opensource.org/licenses/MIT). 31 | 32 | Copyright © 2019 [Borislav Stanimirov](https://ibob.github.io/) 33 | -------------------------------------------------------------------------------- /html/ws-simple.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 55 | 56 | 57 |

Echo

58 | 59 | 60 | 61 | 62 | 63 |
64 | 65 | 66 | -------------------------------------------------------------------------------- /demo-3d/cef/DemoCefRendererApp.cpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #include "DemoCefRendererApp.hpp" 9 | 10 | #include 11 | 12 | jsbind::persistent jsReceiveFunc; 13 | 14 | void jsSetReceiveFunc(jsbind::local func) 15 | { 16 | jsReceiveFunc.reset(func); 17 | } 18 | 19 | void jsSend(std::string text) 20 | { 21 | auto msg = CefProcessMessage::Create("msgFromGUI"); 22 | msg->GetArgumentList()->SetString(0, text); 23 | CefV8Context::GetCurrentContext()->GetFrame()->SendProcessMessage(PID_BROWSER, msg); 24 | } 25 | 26 | JSBIND_BINDINGS(demo3d) 27 | { 28 | jsbind::function("send", jsSend); 29 | jsbind::function("setReceiveFunc", jsSetReceiveFunc); 30 | } 31 | 32 | DemoCefRendererApp::DemoCefRendererApp() = default; 33 | DemoCefRendererApp::~DemoCefRendererApp() = default; 34 | 35 | void DemoCefRendererApp::OnContextCreated(CefRefPtr /*browser*/, 36 | CefRefPtr /*frame*/, CefRefPtr /*context*/) 37 | { 38 | jsbind::initialize(); 39 | } 40 | 41 | void DemoCefRendererApp::OnContextReleased(CefRefPtr /*browser*/, 42 | CefRefPtr /*frame*/, CefRefPtr /*context*/) 43 | { 44 | jsbind::enter_context(); 45 | jsReceiveFunc.reset(); 46 | jsbind::exit_context(); 47 | jsbind::deinitialize(); 48 | } 49 | 50 | bool DemoCefRendererApp::OnProcessMessageReceived(CefRefPtr /*browser*/, CefRefPtr /*frame*/, 51 | CefProcessId /*source_process*/, CefRefPtr message) 52 | { 53 | if (message->GetName() == "msgFromApp") 54 | { 55 | auto text = message->GetArgumentList()->GetString(0).ToString(); 56 | jsbind::enter_context(); 57 | jsReceiveFunc.to_local()(text); 58 | jsbind::exit_context(); 59 | return true; 60 | } 61 | return false; 62 | } 63 | -------------------------------------------------------------------------------- /fs-browser/browser-lib/fsbrowser/FSBrowser.cpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #include "FSBrowser.hpp" 9 | #include "Dir.hpp" 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | namespace fsbrowser 16 | { 17 | 18 | FSBrowser::FSBrowser() 19 | { 20 | auto exePath = DirUtil::getCurrentExecutablePath(); 21 | auto repoPath = DirUtil::getAssetPath(exePath, "html"); 22 | 23 | if (!repoPath.empty()) { 24 | assert(repoPath.size() >= 5); 25 | // remove /html 26 | repoPath = repoPath.substr(0, repoPath.size() - 5); 27 | } 28 | 29 | m_dir.reset(new Dir(repoPath)); 30 | } 31 | 32 | FSBrowser::~FSBrowser() = default; 33 | 34 | std::string FSBrowser::onRequest(const std::string& request) 35 | { 36 | const char* err = R"json({"type":"Error","msg":"Unknown request"})json"; 37 | std::string r = request; 38 | auto f = r.find(':'); 39 | if (f == std::string::npos) return err; 40 | auto rtype = r.substr(0, f); 41 | if (rtype != "contents") return err; 42 | auto rarg = r.substr(f + 1); 43 | return dir(rarg); 44 | } 45 | 46 | std::string FSBrowser::dir(const std::string& path) const 47 | { 48 | auto contents = m_dir->getDirectoryContents(path.c_str()); 49 | 50 | std::ostringstream json; 51 | json << "{"; 52 | json << R"json("type":"Contents","path":")json"; 53 | json << path; 54 | json << R"json(","dirs":[)json"; 55 | for (size_t i = 0; i < contents.dirs.size(); ++i) { 56 | if (i != 0) json << ','; 57 | json << '"' << contents.dirs[i] << '"'; 58 | } 59 | json << R"json(],"files":[)json"; 60 | for (size_t i = 0; i < contents.files.size(); ++i) { 61 | if (i != 0) json << ','; 62 | json << '"' << contents.files[i] << '"'; 63 | } 64 | json << R"json(])json"; 65 | json << "}"; 66 | 67 | return json.str(); 68 | } 69 | 70 | 71 | } 72 | -------------------------------------------------------------------------------- /demo-3d/cef/cef-3d-async-main.cpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #include "DemoCefApp.hpp" 9 | #include "DemoCefRendererApp.hpp" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "URL.h" 18 | 19 | int main(int argc, char* argv[]) 20 | { 21 | CefRefPtr commandLine = CefCommandLine::CreateCommandLine(); 22 | #if defined(_WIN32) 23 | CefEnableHighDPISupport(); 24 | CefMainArgs args(GetModuleHandle(NULL)); 25 | commandLine->InitFromString(GetCommandLineW()); 26 | #else 27 | CefMainArgs args(argc, argv); 28 | commandLine->InitFromArgv(argc, argv); 29 | #endif 30 | 31 | void* windowsSandboxInfo = NULL; 32 | 33 | #if defined(CEF_USE_SANDBOX) && defined(_WIN32) 34 | // Manage the life span of the sandbox information object. This is necessary 35 | // for sandbox support on Windows. See cef_sandbox_win.h for complete details. 36 | CefScopedSandboxInfo scopedSandbox; 37 | windowsSandboxInfo = scopedSandbox.sandbox_info(); 38 | #endif 39 | 40 | CefRefPtr rendererApp = nullptr; 41 | std::string appType = commandLine->GetSwitchValue("type"); 42 | if (appType == "renderer" || appType == "zygote") { 43 | rendererApp = new DemoCefRendererApp; 44 | } 45 | int result = CefExecuteProcess(args, rendererApp, windowsSandboxInfo); 46 | if (result >= 0) 47 | { 48 | // child process completed 49 | return result; 50 | } 51 | 52 | CefSettings settings; 53 | settings.remote_debugging_port = 1234; 54 | settings.windowless_rendering_enabled = 1; 55 | #if !defined(CEF_USE_SANDBOX) 56 | settings.no_sandbox = true; 57 | #endif 58 | 59 | CefInitialize(args, settings, nullptr, windowsSandboxInfo); 60 | 61 | // run sokol demo 62 | int ret = demo::Demo::run(std::make_unique()); 63 | 64 | CefShutdown(); 65 | 66 | return ret; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /helper_lib/helper/DirUtil.cpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #include "DirUtil.hpp" 9 | 10 | #if defined(_WIN32) 11 | # include 12 | #else 13 | # include 14 | # include 15 | #endif 16 | 17 | #include 18 | #include 19 | 20 | #if defined(_WIN32) 21 | namespace 22 | { 23 | // local function used to identify the current module on Windows 24 | void getAddr() {} 25 | } 26 | #endif 27 | 28 | std::string DirUtil::getCurrentExecutablePath() 29 | { 30 | std::string modulePath; 31 | #if defined(_WIN32) 32 | HMODULE engine; 33 | GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | 34 | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 35 | (LPCSTR)getAddr, 36 | &engine); 37 | 38 | char path[_MAX_PATH + _MAX_FNAME + 1]; 39 | GetModuleFileNameA(engine, path, _MAX_PATH + _MAX_FNAME); 40 | 41 | // normalize path 42 | char* p = path; 43 | while (*p) 44 | { 45 | if (*p == '\\') 46 | { 47 | *p = '/'; 48 | } 49 | ++p; 50 | } 51 | 52 | modulePath = path; 53 | 54 | #else 55 | // retrieve the executable path and hope for the best 56 | char buff[2048]; 57 | size_t len = readlink("/proc/self/exe", buff, sizeof(buff) - 1); 58 | if (len > 0) 59 | { 60 | buff[len] = 0; 61 | modulePath = buff; 62 | } 63 | 64 | #endif 65 | 66 | return modulePath; 67 | } 68 | 69 | std::string DirUtil::getAssetPath(std::string baseDir, const std::string& assetDir) 70 | { 71 | while (true) 72 | { 73 | auto slash = baseDir.rfind('/'); 74 | if (slash == std::string::npos) 75 | { 76 | baseDir = assetDir; 77 | break; 78 | } 79 | 80 | baseDir.erase(slash + 1); 81 | 82 | baseDir += assetDir; 83 | 84 | struct stat info; 85 | if (stat(baseDir.c_str(), &info) == 0 && (info.st_mode & S_IFDIR)) 86 | { 87 | break; 88 | } 89 | 90 | baseDir.erase(slash); 91 | } 92 | 93 | return baseDir; 94 | } 95 | -------------------------------------------------------------------------------- /html/fs-browser.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | File Manager :: CEF Demos 6 | 7 | 51 | 52 | 53 | 54 | 55 |
App type:
56 |
57 |
Path:
58 |
59 | 60 | 61 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /html/game-gui.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Game GUI 4 | 145 | 146 | 147 |

Box Rotation GUI

148 |
149 |

Revolutions

150 |
151 |
x: 0
152 |
y: 0
153 |
z: 0
154 |
155 |

Rotation

156 |
157 | 162 |
163 |

Rotation Axis

164 |
165 | 170 | 175 | 180 |
181 |

Rotation Speed

182 |
183 |
-
184 |
1.000
185 |
+
186 |
187 |
188 | 189 | 217 | -------------------------------------------------------------------------------- /ws-simple/ws_simple_server.cpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | using namespace std; 26 | using namespace std::chrono_literals; 27 | 28 | namespace beast = boost::beast; // from 29 | namespace http = beast::http; // from 30 | namespace websocket = beast::websocket; // from 31 | namespace net = boost::asio; // from 32 | using tcp = boost::asio::ip::tcp; // from 33 | 34 | atomic_uint32_t sid = 0; 35 | 36 | struct Packet 37 | { 38 | string textBuffer; 39 | vector binaryBuffer; 40 | bool text = false; 41 | }; 42 | 43 | using PacketPtr = shared_ptr; 44 | 45 | class Session : public enable_shared_from_this 46 | { 47 | public: 48 | Session(tcp::socket&& socket) 49 | : m_id(++sid) 50 | , m_ws(move(socket)) 51 | { 52 | // set suggested timeout settings for the websocket 53 | m_ws.set_option(websocket::stream_base::timeout::suggested(beast::role_type::server)); 54 | 55 | // set a decorator to change the Server of the handshake 56 | m_ws.set_option(websocket::stream_base::decorator( 57 | [](websocket::response_type& res) 58 | { 59 | res.set(http::field::server, string(BOOST_BEAST_VERSION_STRING) + "ws-simple-server"); 60 | })); 61 | 62 | cout << "Created session " << m_id << '\n'; 63 | } 64 | 65 | void run() 66 | { 67 | m_ws.async_accept(beast::bind_front_handler(&Session::onAccept, shared_from_this())); 68 | } 69 | 70 | void onAccept(beast::error_code e) 71 | { 72 | if (e) return fail(e, "accept"); 73 | doRead(); 74 | } 75 | 76 | void doRead() 77 | { 78 | m_ws.async_read(m_readBuf, beast::bind_front_handler(&Session::onRead, shared_from_this())); 79 | } 80 | 81 | void onRead(beast::error_code e, size_t /*length*/) 82 | { 83 | if (e == websocket::error::closed) return close(); 84 | if (e) return fail(e, "read"); 85 | 86 | if (!m_ws.got_text()) 87 | { 88 | cout << "o_O Got non text. Ignoring.\n"; 89 | } 90 | else 91 | { 92 | auto packet = make_shared(); 93 | packet->text = true; 94 | auto data = reinterpret_cast(m_readBuf.cdata().data()); 95 | packet->textBuffer.assign(data, data + m_readBuf.size()); 96 | onReceive(packet); 97 | } 98 | 99 | m_readBuf.clear(); 100 | doRead(); // read loop 101 | } 102 | 103 | void onReceive(const PacketPtr& packet) 104 | { 105 | cout << "Received: " << packet->textBuffer << '\n'; 106 | // echo 107 | send(packet); 108 | } 109 | 110 | void send(const PacketPtr& packet) 111 | { 112 | m_writeQueue.emplace_back(packet); 113 | if (m_writeQueue.size() > 1) return; // we're already writing 114 | 115 | doWrite(); 116 | } 117 | 118 | void doWrite() 119 | { 120 | assert(!m_writeQueue.empty()); 121 | 122 | auto& packet = m_writeQueue.front(); 123 | m_ws.text(packet->text); 124 | auto handler = beast::bind_front_handler(&Session::onWrite, shared_from_this()); 125 | if (packet->text) m_ws.async_write(net::buffer(packet->textBuffer), std::move(handler)); 126 | else m_ws.async_write(net::buffer(packet->binaryBuffer), std::move(handler)); 127 | } 128 | 129 | void onWrite(beast::error_code e, std::size_t) 130 | { 131 | if (e) return fail(e, "write"); 132 | 133 | m_writeQueue.pop_front(); 134 | if (m_writeQueue.empty()) return; 135 | 136 | doWrite(); 137 | } 138 | 139 | void fail(beast::error_code e, const char* source) 140 | { 141 | cerr << "Session " << m_id << " error: " << e << " in " << source << '\n'; 142 | } 143 | 144 | void close() 145 | { 146 | cout << "Session " << m_id << " closed \n"; 147 | } 148 | 149 | private: 150 | const uint32_t m_id; 151 | websocket::stream m_ws; 152 | 153 | // io 154 | beast::flat_buffer m_readBuf, m_writeBuf; 155 | 156 | deque m_writeQueue; 157 | }; 158 | 159 | class Server 160 | { 161 | public: 162 | Server(tcp::endpoint endpoint) 163 | : m_context(1) 164 | , m_acceptor(m_context, endpoint) 165 | {} 166 | 167 | int run() 168 | { 169 | doAccept(); 170 | m_context.run(); 171 | return 0; 172 | } 173 | 174 | void doAccept() 175 | { 176 | m_acceptor.async_accept(beast::bind_front_handler(&Server::onAccept, this)); 177 | } 178 | 179 | void onAccept(beast::error_code error, tcp::socket socket) 180 | { 181 | if (error) 182 | { 183 | cerr << "Server::onAccept error: " << error << '\n'; 184 | return; 185 | } 186 | 187 | auto session = make_shared(move(socket)); 188 | session->run(); 189 | 190 | doAccept(); 191 | } 192 | 193 | private: 194 | net::io_context m_context; 195 | tcp::acceptor m_acceptor; 196 | }; 197 | 198 | int main() 199 | { 200 | /////////////////////////////////////////////////////////////////////////// 201 | // args 202 | const auto address = boost::asio::ip::tcp::v4(); // net::ip::make_address(argAddr); 203 | const uint16_t port = 7654; 204 | 205 | /////////////////////////////////////////////////////////////////////////// 206 | // run 207 | Server server(tcp::endpoint(address, port)); 208 | return server.run(); 209 | } -------------------------------------------------------------------------------- /fs-browser/ws-server/fs-ws-server.cpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "fsbrowser/FSBrowser.hpp" 26 | 27 | using namespace std; 28 | using namespace std::chrono_literals; 29 | 30 | namespace beast = boost::beast; // from 31 | namespace http = beast::http; // from 32 | namespace websocket = beast::websocket; // from 33 | namespace net = boost::asio; // from 34 | using tcp = boost::asio::ip::tcp; // from 35 | 36 | atomic_uint32_t sid = 0; 37 | 38 | struct Packet 39 | { 40 | string textBuffer; 41 | vector binaryBuffer; 42 | bool text = false; 43 | }; 44 | 45 | using PacketPtr = shared_ptr; 46 | 47 | class Session : public enable_shared_from_this 48 | { 49 | public: 50 | Session(tcp::socket&& socket) 51 | : m_id(++sid) 52 | , m_ws(move(socket)) 53 | { 54 | // set suggested timeout settings for the websocket 55 | m_ws.set_option(websocket::stream_base::timeout::suggested(beast::role_type::server)); 56 | 57 | // set a decorator to change the Server of the handshake 58 | m_ws.set_option(websocket::stream_base::decorator( 59 | [](websocket::response_type& res) 60 | { 61 | res.set(http::field::server, string(BOOST_BEAST_VERSION_STRING) + "ws-simple-server"); 62 | })); 63 | 64 | cout << "Created session " << m_id << '\n'; 65 | } 66 | 67 | void run() 68 | { 69 | m_ws.async_accept(beast::bind_front_handler(&Session::onAccept, shared_from_this())); 70 | } 71 | 72 | void onAccept(beast::error_code e) 73 | { 74 | if (e) return fail(e, "accept"); 75 | doRead(); 76 | } 77 | 78 | void doRead() 79 | { 80 | m_ws.async_read(m_readBuf, beast::bind_front_handler(&Session::onRead, shared_from_this())); 81 | } 82 | 83 | void onRead(beast::error_code e, size_t /*length*/) 84 | { 85 | if (e == websocket::error::closed) return close(); 86 | if (e) return fail(e, "read"); 87 | 88 | if (!m_ws.got_text()) 89 | { 90 | cout << "o_O Got non text. Ignoring.\n"; 91 | } 92 | else 93 | { 94 | auto packet = make_shared(); 95 | packet->text = true; 96 | auto data = reinterpret_cast(m_readBuf.cdata().data()); 97 | packet->textBuffer.assign(data, data + m_readBuf.size()); 98 | onReceive(packet); 99 | } 100 | 101 | m_readBuf.clear(); 102 | doRead(); // read loop 103 | } 104 | 105 | void onReceive(const PacketPtr& packet) 106 | { 107 | cout << "Received: " << packet->textBuffer << '\n'; 108 | 109 | auto resp = m_fsBrowser.onRequest(packet->textBuffer); 110 | 111 | auto respPacket = make_shared(); 112 | respPacket->text = true; 113 | respPacket->textBuffer = resp; 114 | 115 | send(respPacket); 116 | } 117 | 118 | void send(const PacketPtr& packet) 119 | { 120 | m_writeQueue.emplace_back(packet); 121 | if (m_writeQueue.size() > 1) return; // we're already writing 122 | 123 | doWrite(); 124 | } 125 | 126 | void doWrite() 127 | { 128 | assert(!m_writeQueue.empty()); 129 | 130 | auto& packet = m_writeQueue.front(); 131 | m_ws.text(packet->text); 132 | auto handler = beast::bind_front_handler(&Session::onWrite, shared_from_this()); 133 | if (packet->text) m_ws.async_write(net::buffer(packet->textBuffer), std::move(handler)); 134 | else m_ws.async_write(net::buffer(packet->binaryBuffer), std::move(handler)); 135 | } 136 | 137 | void onWrite(beast::error_code e, std::size_t) 138 | { 139 | if (e) return fail(e, "write"); 140 | 141 | m_writeQueue.pop_front(); 142 | if (m_writeQueue.empty()) return; 143 | 144 | doWrite(); 145 | } 146 | 147 | void fail(beast::error_code e, const char* source) 148 | { 149 | cerr << "Session " << m_id << " error: " << e << " in " << source << '\n'; 150 | } 151 | 152 | void close() 153 | { 154 | cout << "Session " << m_id << " closed \n"; 155 | } 156 | 157 | private: 158 | const uint32_t m_id; 159 | websocket::stream m_ws; 160 | 161 | // io 162 | beast::flat_buffer m_readBuf, m_writeBuf; 163 | 164 | deque m_writeQueue; 165 | 166 | fsbrowser::FSBrowser m_fsBrowser; 167 | }; 168 | 169 | class Server 170 | { 171 | public: 172 | Server(tcp::endpoint endpoint) 173 | : m_context(1) 174 | , m_acceptor(m_context, endpoint) 175 | {} 176 | 177 | int run() 178 | { 179 | doAccept(); 180 | m_context.run(); 181 | return 0; 182 | } 183 | 184 | void doAccept() 185 | { 186 | m_acceptor.async_accept(beast::bind_front_handler(&Server::onAccept, this)); 187 | } 188 | 189 | void onAccept(beast::error_code error, tcp::socket socket) 190 | { 191 | if (error) 192 | { 193 | cerr << "Server::onAccept error: " << error << '\n'; 194 | return; 195 | } 196 | 197 | auto session = make_shared(move(socket)); 198 | session->run(); 199 | 200 | doAccept(); 201 | } 202 | 203 | private: 204 | net::io_context m_context; 205 | tcp::acceptor m_acceptor; 206 | }; 207 | 208 | int main() 209 | { 210 | /////////////////////////////////////////////////////////////////////////// 211 | // args 212 | const auto address = boost::asio::ip::tcp::v4(); // net::ip::make_address(argAddr); 213 | const uint16_t port = 7654; 214 | 215 | /////////////////////////////////////////////////////////////////////////// 216 | // run 217 | Server server(tcp::endpoint(address, port)); 218 | return server.run(); 219 | } -------------------------------------------------------------------------------- /cef-sync/cef-sync.cpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #define URI_ROOT "http://htmldemos" 20 | const char* const URL = URI_ROOT "/cef-echo.html"; 21 | 22 | void setupResourceManagerDirectoryProvider(CefRefPtr resource_manager, std::string uri, std::string dir) 23 | { 24 | if (!CefCurrentlyOn(TID_IO)) { 25 | // Execute on the browser IO thread. 26 | CefPostTask(TID_IO, base::Bind(&setupResourceManagerDirectoryProvider, resource_manager, uri, dir)); 27 | return; 28 | } 29 | 30 | resource_manager->AddDirectoryProvider(uri, dir, 1, dir); 31 | } 32 | 33 | // this is only needed so we have a way to break the message loop 34 | class MinimalClient : public CefClient, public CefLifeSpanHandler, public CefRequestHandler, public CefResourceRequestHandler 35 | { 36 | public: 37 | MinimalClient() 38 | : m_resourceManager(new CefResourceManager) 39 | { 40 | auto exePath = DirUtil::getCurrentExecutablePath(); 41 | auto assetPath = DirUtil::getAssetPath(exePath, "html"); 42 | setupResourceManagerDirectoryProvider(m_resourceManager, URI_ROOT, assetPath); 43 | } 44 | 45 | CefRefPtr GetLifeSpanHandler() override { return this; } 46 | CefRefPtr GetRequestHandler() override { return this; } 47 | 48 | void OnBeforeClose(CefRefPtr /*browser*/) override 49 | { 50 | CefQuitMessageLoop(); 51 | } 52 | 53 | CefRefPtr GetResourceRequestHandler( 54 | CefRefPtr /*browser*/, 55 | CefRefPtr /*frame*/, 56 | CefRefPtr /*request*/, 57 | bool /*is_navigation*/, 58 | bool /*is_download*/, 59 | const CefString& /*request_initiator*/, 60 | bool& /*disable_default_handling*/) override { 61 | return this; 62 | } 63 | 64 | cef_return_value_t OnBeforeResourceLoad( 65 | CefRefPtr browser, 66 | CefRefPtr frame, 67 | CefRefPtr request, 68 | CefRefPtr callback) override 69 | { 70 | return m_resourceManager->OnBeforeResourceLoad(browser, frame, request, callback); 71 | } 72 | 73 | CefRefPtr GetResourceHandler( 74 | CefRefPtr browser, 75 | CefRefPtr frame, 76 | CefRefPtr request) override 77 | { 78 | return m_resourceManager->GetResourceHandler(browser, frame, request); 79 | } 80 | 81 | private: 82 | CefRefPtr m_resourceManager; 83 | 84 | IMPLEMENT_REFCOUNTING(MinimalClient); 85 | DISALLOW_COPY_AND_ASSIGN(MinimalClient); 86 | }; 87 | 88 | jsbind::persistent jsOnReceiveFunc; 89 | 90 | void setReceiveFunc(jsbind::local func) 91 | { 92 | jsOnReceiveFunc.reset(func); 93 | } 94 | 95 | void echo(std::string text) 96 | { 97 | std::cout << "Called echo with text: " << text << std::endl; 98 | jsOnReceiveFunc.to_local()(text); 99 | } 100 | 101 | JSBIND_BINDINGS(App) 102 | { 103 | jsbind::function("echo", echo); 104 | jsbind::function("setReceiveFunc", setReceiveFunc); 105 | } 106 | 107 | class RendererApp : public CefApp, public CefRenderProcessHandler 108 | { 109 | public: 110 | RendererApp() = default; 111 | 112 | CefRefPtr GetRenderProcessHandler() override 113 | { 114 | return this; 115 | } 116 | 117 | void OnContextCreated(CefRefPtr /*browser*/, CefRefPtr /*frame*/, CefRefPtr /*context*/) override 118 | { 119 | jsbind::initialize(); 120 | } 121 | 122 | void OnContextReleased(CefRefPtr /*browser*/, CefRefPtr /*frame*/, CefRefPtr /*context*/) override 123 | { 124 | jsbind::enter_context(); 125 | jsOnReceiveFunc.reset(); 126 | jsbind::exit_context(); 127 | jsbind::deinitialize(); 128 | } 129 | 130 | private: 131 | IMPLEMENT_REFCOUNTING(RendererApp); 132 | DISALLOW_COPY_AND_ASSIGN(RendererApp); 133 | }; 134 | 135 | int main(int argc, char* argv[]) 136 | { 137 | CefRefPtr commandLine = CefCommandLine::CreateCommandLine(); 138 | #if defined(_WIN32) 139 | CefEnableHighDPISupport(); 140 | CefMainArgs args(GetModuleHandle(NULL)); 141 | commandLine->InitFromString(GetCommandLineW()); 142 | #else 143 | CefMainArgs args(argc, argv); 144 | commandLine->InitFromArgv(argc, argv); 145 | #endif 146 | 147 | void* windowsSandboxInfo = NULL; 148 | 149 | #if defined(CEF_USE_SANDBOX) && defined(_WIN32) 150 | // Manage the life span of the sandbox information object. This is necessary 151 | // for sandbox support on Windows. See cef_sandbox_win.h for complete details. 152 | CefScopedSandboxInfo scopedSandbox; 153 | windowsSandboxInfo = scopedSandbox.sandbox_info(); 154 | #endif 155 | 156 | CefRefPtr app = nullptr; 157 | std::string appType = commandLine->GetSwitchValue("type"); 158 | if (appType == "renderer" || appType == "zygote" || appType.empty()) 159 | { 160 | app = new RendererApp; 161 | // use nullptr for other process types 162 | } 163 | int result = CefExecuteProcess(args, app, windowsSandboxInfo); 164 | if (result >= 0) 165 | { 166 | // child process completed 167 | return result; 168 | } 169 | 170 | CefSettings settings; 171 | settings.remote_debugging_port = 1234; 172 | #if !defined(CEF_USE_SANDBOX) 173 | settings.no_sandbox = true; 174 | #endif 175 | 176 | CefInitialize(args, settings, app, windowsSandboxInfo); 177 | 178 | CefWindowInfo windowInfo; 179 | 180 | #if defined(_WIN32) 181 | // On Windows we need to specify certain flags that will be passed to CreateWindowEx(). 182 | windowInfo.SetAsPopup(NULL, "simple"); 183 | #endif 184 | CefBrowserSettings browserSettings; 185 | CefBrowserHost::CreateBrowser(windowInfo, new MinimalClient, URL, browserSettings, nullptr, nullptr); 186 | 187 | CefRunMessageLoop(); 188 | 189 | CefShutdown(); 190 | 191 | return 0; 192 | } 193 | -------------------------------------------------------------------------------- /cef-async/cef-async.cpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #define URI_ROOT "http://htmldemos" 20 | const char* const URL = URI_ROOT "/cef-echo.html"; 21 | 22 | void setupResourceManagerDirectoryProvider(CefRefPtr resource_manager, std::string uri, std::string dir) 23 | { 24 | if (!CefCurrentlyOn(TID_IO)) { 25 | // Execute on the browser IO thread. 26 | CefPostTask(TID_IO, base::Bind(&setupResourceManagerDirectoryProvider,resource_manager, uri, dir)); 27 | return; 28 | } 29 | 30 | resource_manager->AddDirectoryProvider(uri, dir, 1, dir); 31 | } 32 | 33 | // this is only needed so we have a way to break the message loop 34 | class MinimalClient : public CefClient, public CefLifeSpanHandler, public CefRequestHandler, public CefResourceRequestHandler 35 | { 36 | public: 37 | MinimalClient() 38 | : m_resourceManager(new CefResourceManager) 39 | { 40 | auto exePath = DirUtil::getCurrentExecutablePath(); 41 | auto assetPath = DirUtil::getAssetPath(exePath, "html"); 42 | setupResourceManagerDirectoryProvider(m_resourceManager, URI_ROOT, assetPath); 43 | } 44 | 45 | CefRefPtr GetLifeSpanHandler() override { return this; } 46 | CefRefPtr GetRequestHandler() override { return this; } 47 | 48 | void OnBeforeClose(CefRefPtr /*browser*/) override 49 | { 50 | CefQuitMessageLoop(); 51 | } 52 | 53 | CefRefPtr GetResourceRequestHandler( 54 | CefRefPtr /*browser*/, 55 | CefRefPtr /*frame*/, 56 | CefRefPtr /*request*/, 57 | bool /*is_navigation*/, 58 | bool /*is_download*/, 59 | const CefString& /*request_initiator*/, 60 | bool& /*disable_default_handling*/) override { 61 | return this; 62 | } 63 | 64 | cef_return_value_t OnBeforeResourceLoad( 65 | CefRefPtr browser, 66 | CefRefPtr frame, 67 | CefRefPtr request, 68 | CefRefPtr callback) override 69 | { 70 | return m_resourceManager->OnBeforeResourceLoad(browser, frame, request, callback); 71 | } 72 | 73 | CefRefPtr GetResourceHandler( 74 | CefRefPtr browser, 75 | CefRefPtr frame, 76 | CefRefPtr request) override 77 | { 78 | return m_resourceManager->GetResourceHandler(browser, frame, request); 79 | } 80 | 81 | bool OnProcessMessageReceived(CefRefPtr /*browser*/, 82 | CefRefPtr frame, 83 | CefProcessId /*source_process*/, 84 | CefRefPtr message) override 85 | { 86 | if (message->GetName() == "onEcho") 87 | { 88 | auto text = message->GetArgumentList()->GetString(0).ToString(); 89 | std::cout << "onEcho received: " << text << std::endl; 90 | auto response = CefProcessMessage::Create("onEchoResponse"); 91 | response->GetArgumentList()->SetString(0, text); 92 | frame->SendProcessMessage(PID_RENDERER, response); 93 | return true; 94 | } 95 | return false; 96 | } 97 | 98 | private: 99 | CefRefPtr m_resourceManager; 100 | 101 | IMPLEMENT_REFCOUNTING(MinimalClient); 102 | DISALLOW_COPY_AND_ASSIGN(MinimalClient); 103 | }; 104 | 105 | jsbind::persistent jsOnReceiveFunc; 106 | 107 | void setReceiveFunc(jsbind::local func) 108 | { 109 | jsOnReceiveFunc.reset(func); 110 | } 111 | 112 | void echo(std::string text) 113 | { 114 | auto msg = CefProcessMessage::Create("onEcho"); 115 | msg->GetArgumentList()->SetString(0, text); 116 | CefV8Context::GetCurrentContext()->GetFrame()->SendProcessMessage(PID_BROWSER, msg); 117 | } 118 | 119 | JSBIND_BINDINGS(App) 120 | { 121 | jsbind::function("echo", echo); 122 | jsbind::function("setReceiveFunc", setReceiveFunc); 123 | } 124 | 125 | class RendererApp : public CefApp, public CefRenderProcessHandler 126 | { 127 | public: 128 | RendererApp() = default; 129 | 130 | CefRefPtr GetRenderProcessHandler() override 131 | { 132 | return this; 133 | } 134 | 135 | void OnContextCreated(CefRefPtr /*browser*/, CefRefPtr /*frame*/, CefRefPtr /*context*/) override 136 | { 137 | jsbind::initialize(); 138 | } 139 | 140 | void OnContextReleased(CefRefPtr /*browser*/, CefRefPtr /*frame*/, CefRefPtr /*context*/) override 141 | { 142 | jsbind::enter_context(); 143 | jsOnReceiveFunc.reset(); 144 | jsbind::exit_context(); 145 | jsbind::deinitialize(); 146 | } 147 | 148 | bool OnProcessMessageReceived(CefRefPtr /*browser*/, 149 | CefRefPtr /*frame*/, 150 | CefProcessId /*source_process*/, 151 | CefRefPtr message) override 152 | { 153 | if (message->GetName() == "onEchoResponse") 154 | { 155 | auto text = message->GetArgumentList()->GetString(0).ToString(); 156 | jsbind::enter_context(); 157 | jsOnReceiveFunc.to_local()(text); 158 | jsbind::exit_context(); 159 | return true; 160 | } 161 | return false; 162 | } 163 | private: 164 | IMPLEMENT_REFCOUNTING(RendererApp); 165 | DISALLOW_COPY_AND_ASSIGN(RendererApp); 166 | }; 167 | 168 | int main(int argc, char* argv[]) 169 | { 170 | CefRefPtr commandLine = CefCommandLine::CreateCommandLine(); 171 | #if defined(_WIN32) 172 | CefEnableHighDPISupport(); 173 | CefMainArgs args(GetModuleHandle(NULL)); 174 | commandLine->InitFromString(GetCommandLineW()); 175 | #else 176 | CefMainArgs args(argc, argv); 177 | commandLine->InitFromArgv(argc, argv); 178 | #endif 179 | 180 | void* windowsSandboxInfo = NULL; 181 | 182 | #if defined(CEF_USE_SANDBOX) && defined(_WIN32) 183 | // Manage the life span of the sandbox information object. This is necessary 184 | // for sandbox support on Windows. See cef_sandbox_win.h for complete details. 185 | CefScopedSandboxInfo scopedSandbox; 186 | windowsSandboxInfo = scopedSandbox.sandbox_info(); 187 | #endif 188 | 189 | CefRefPtr app = nullptr; 190 | std::string appType = commandLine->GetSwitchValue("type"); 191 | if (appType == "renderer" || appType == "zygote") 192 | { 193 | app = new RendererApp; 194 | // use nullptr for other process types 195 | } 196 | int result = CefExecuteProcess(args, app, windowsSandboxInfo); 197 | if (result >= 0) 198 | { 199 | // child process completed 200 | return result; 201 | } 202 | 203 | CefSettings settings; 204 | settings.remote_debugging_port = 1234; 205 | #if !defined(CEF_USE_SANDBOX) 206 | settings.no_sandbox = true; 207 | #endif 208 | 209 | CefInitialize(args, settings, nullptr, windowsSandboxInfo); 210 | 211 | CefWindowInfo windowInfo; 212 | 213 | #if defined(_WIN32) 214 | // On Windows we need to specify certain flags that will be passed to CreateWindowEx(). 215 | windowInfo.SetAsPopup(NULL, "simple"); 216 | #endif 217 | CefBrowserSettings browserSettings; 218 | CefBrowserHost::CreateBrowser(windowInfo, new MinimalClient, URL, browserSettings, nullptr, nullptr); 219 | 220 | CefRunMessageLoop(); 221 | 222 | CefShutdown(); 223 | 224 | return 0; 225 | } 226 | -------------------------------------------------------------------------------- /fs-browser/cef/fs-cef.cpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | #define URI_ROOT "http://htmldemos" 22 | const char* const URL = URI_ROOT "/fs-browser.html"; 23 | 24 | void setupResourceManagerDirectoryProvider(CefRefPtr resource_manager, std::string uri, std::string dir) 25 | { 26 | if (!CefCurrentlyOn(TID_IO)) { 27 | // Execute on the browser IO thread. 28 | CefPostTask(TID_IO, base::Bind(&setupResourceManagerDirectoryProvider, resource_manager, uri, dir)); 29 | return; 30 | } 31 | 32 | resource_manager->AddDirectoryProvider(uri, dir, 1, dir); 33 | } 34 | 35 | // this is only needed so we have a way to break the message loop 36 | class FSBrowserClient : public CefClient, public CefLifeSpanHandler, public CefRequestHandler, public CefResourceRequestHandler 37 | { 38 | public: 39 | FSBrowserClient() 40 | : m_resourceManager(new CefResourceManager) 41 | { 42 | auto exePath = DirUtil::getCurrentExecutablePath(); 43 | auto assetPath = DirUtil::getAssetPath(exePath, "html"); 44 | setupResourceManagerDirectoryProvider(m_resourceManager, URI_ROOT, assetPath); 45 | } 46 | 47 | CefRefPtr GetLifeSpanHandler() override { return this; } 48 | CefRefPtr GetRequestHandler() override { return this; } 49 | 50 | void OnBeforeClose(CefRefPtr /*browser*/) override 51 | { 52 | CefQuitMessageLoop(); 53 | } 54 | 55 | CefRefPtr GetResourceRequestHandler( 56 | CefRefPtr /*browser*/, 57 | CefRefPtr /*frame*/, 58 | CefRefPtr /*request*/, 59 | bool /*is_navigation*/, 60 | bool /*is_download*/, 61 | const CefString& /*request_initiator*/, 62 | bool& /*disable_default_handling*/) override { 63 | return this; 64 | } 65 | 66 | cef_return_value_t OnBeforeResourceLoad( 67 | CefRefPtr browser, 68 | CefRefPtr frame, 69 | CefRefPtr request, 70 | CefRefPtr callback) override 71 | { 72 | return m_resourceManager->OnBeforeResourceLoad(browser, frame, request, callback); 73 | } 74 | 75 | CefRefPtr GetResourceHandler( 76 | CefRefPtr browser, 77 | CefRefPtr frame, 78 | CefRefPtr request) override 79 | { 80 | return m_resourceManager->GetResourceHandler(browser, frame, request); 81 | } 82 | 83 | bool OnProcessMessageReceived(CefRefPtr /*browser*/, 84 | CefRefPtr frame, 85 | CefProcessId /*source_process*/, 86 | CefRefPtr message) override 87 | { 88 | if (message->GetName() == "onRequest") 89 | { 90 | auto text = message->GetArgumentList()->GetString(0).ToString(); 91 | std::cout << "request received: " << text << std::endl; 92 | 93 | auto resp = m_fsBrowser.onRequest(text); 94 | 95 | auto response = CefProcessMessage::Create("onResponse"); 96 | response->GetArgumentList()->SetString(0, resp); 97 | frame->SendProcessMessage(PID_RENDERER, response); 98 | 99 | return true; 100 | } 101 | return false; 102 | } 103 | 104 | private: 105 | CefRefPtr m_resourceManager; 106 | 107 | fsbrowser::FSBrowser m_fsBrowser; 108 | 109 | IMPLEMENT_REFCOUNTING(FSBrowserClient); 110 | DISALLOW_COPY_AND_ASSIGN(FSBrowserClient); 111 | }; 112 | 113 | jsbind::persistent jsOnResponseFunc; 114 | 115 | void setOnResponse(jsbind::local func) 116 | { 117 | jsOnResponseFunc.reset(func); 118 | } 119 | 120 | void request(std::string text) 121 | { 122 | auto msg = CefProcessMessage::Create("onRequest"); 123 | msg->GetArgumentList()->SetString(0, text); 124 | CefV8Context::GetCurrentContext()->GetFrame()->SendProcessMessage(PID_BROWSER, msg); 125 | } 126 | 127 | JSBIND_BINDINGS(App) 128 | { 129 | jsbind::function("request", request); 130 | jsbind::function("setOnResponse", setOnResponse); 131 | } 132 | 133 | class RendererApp : public CefApp, public CefRenderProcessHandler 134 | { 135 | public: 136 | RendererApp() = default; 137 | 138 | CefRefPtr GetRenderProcessHandler() override 139 | { 140 | return this; 141 | } 142 | 143 | void OnContextCreated(CefRefPtr /*browser*/, CefRefPtr /*frame*/, CefRefPtr /*context*/) override 144 | { 145 | jsbind::initialize(); 146 | } 147 | 148 | void OnContextReleased(CefRefPtr /*browser*/, CefRefPtr /*frame*/, CefRefPtr /*context*/) override 149 | { 150 | jsbind::enter_context(); 151 | jsOnResponseFunc.reset(); 152 | jsbind::exit_context(); 153 | jsbind::deinitialize(); 154 | } 155 | 156 | bool OnProcessMessageReceived(CefRefPtr /*browser*/, 157 | CefRefPtr /*frame*/, 158 | CefProcessId /*source_process*/, 159 | CefRefPtr message) override 160 | { 161 | if (message->GetName() == "onResponse") 162 | { 163 | auto text = message->GetArgumentList()->GetString(0).ToString(); 164 | jsbind::enter_context(); 165 | jsOnResponseFunc.to_local()(text); 166 | jsbind::exit_context(); 167 | return true; 168 | } 169 | return false; 170 | } 171 | private: 172 | IMPLEMENT_REFCOUNTING(RendererApp); 173 | DISALLOW_COPY_AND_ASSIGN(RendererApp); 174 | }; 175 | 176 | int main(int argc, char* argv[]) 177 | { 178 | CefRefPtr commandLine = CefCommandLine::CreateCommandLine(); 179 | #if defined(_WIN32) 180 | CefEnableHighDPISupport(); 181 | CefMainArgs args(GetModuleHandle(NULL)); 182 | commandLine->InitFromString(GetCommandLineW()); 183 | #else 184 | CefMainArgs args(argc, argv); 185 | commandLine->InitFromArgv(argc, argv); 186 | #endif 187 | 188 | void* windowsSandboxInfo = NULL; 189 | 190 | #if defined(CEF_USE_SANDBOX) && defined(_WIN32) 191 | // Manage the life span of the sandbox information object. This is necessary 192 | // for sandbox support on Windows. See cef_sandbox_win.h for complete details. 193 | CefScopedSandboxInfo scopedSandbox; 194 | windowsSandboxInfo = scopedSandbox.sandbox_info(); 195 | #endif 196 | 197 | CefRefPtr app = nullptr; 198 | std::string appType = commandLine->GetSwitchValue("type"); 199 | if (appType == "renderer" || appType == "zygote") 200 | { 201 | app = new RendererApp; 202 | // use nullptr for other process types 203 | } 204 | int result = CefExecuteProcess(args, app, windowsSandboxInfo); 205 | if (result >= 0) 206 | { 207 | // child process completed 208 | return result; 209 | } 210 | 211 | CefSettings settings; 212 | settings.remote_debugging_port = 1234; 213 | #if !defined(CEF_USE_SANDBOX) 214 | settings.no_sandbox = true; 215 | #endif 216 | 217 | CefInitialize(args, settings, nullptr, windowsSandboxInfo); 218 | 219 | CefWindowInfo windowInfo; 220 | 221 | #if defined(_WIN32) 222 | // On Windows we need to specify certain flags that will be passed to CreateWindowEx(). 223 | windowInfo.SetAsPopup(NULL, "simple"); 224 | #endif 225 | CefBrowserSettings browserSettings; 226 | CefBrowserHost::CreateBrowser(windowInfo, new FSBrowserClient, URL, browserSettings, nullptr, nullptr); 227 | 228 | CefRunMessageLoop(); 229 | 230 | CefShutdown(); 231 | 232 | return 0; 233 | } 234 | -------------------------------------------------------------------------------- /demo-3d/cef/DemoCefApp.cpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #include "DemoCefApp.hpp" 9 | #include "URL.h" 10 | 11 | #include 12 | 13 | #include 14 | 15 | #define SOKOL_NO_DEPRECATED 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | namespace 26 | { 27 | void setupResourceManagerDirectoryProvider(CefRefPtr resource_manager, std::string uri, std::string dir) 28 | { 29 | if (!CefCurrentlyOn(TID_IO)) { 30 | // Execute on the browser IO thread. 31 | CefPostTask(TID_IO, base::Bind(&setupResourceManagerDirectoryProvider,resource_manager, uri, dir)); 32 | return; 33 | } 34 | 35 | resource_manager->AddDirectoryProvider(uri, dir, 1, dir); 36 | } 37 | 38 | const char* cef_vs_src = R"glsl( 39 | #version 330 40 | in vec2 position; 41 | in vec2 texcoord; 42 | out vec2 uv; 43 | void main() { 44 | gl_Position = vec4(position, 0, 1); 45 | uv = texcoord; 46 | }; 47 | )glsl"; 48 | 49 | const char* cef_fs_src = R"glsl( 50 | #version 330 51 | uniform sampler2D tex; 52 | in vec2 uv; 53 | out vec4 frag_color; 54 | void main() { 55 | frag_color = texture(tex, uv); 56 | }; 57 | )glsl"; 58 | 59 | struct float3 60 | { 61 | float x, y, z; 62 | }; 63 | 64 | struct float2 65 | { 66 | float x, y; 67 | }; 68 | 69 | struct CefVertex 70 | { 71 | float2 pos; 72 | float2 uv; 73 | }; 74 | 75 | class CefGUI : public demo::GUI, public CefClient, public CefRequestHandler, public CefResourceRequestHandler, public CefRenderHandler 76 | { 77 | public: 78 | CefGUI() 79 | : m_resourceManager(new CefResourceManager) 80 | { 81 | { 82 | auto exePath = DirUtil::getCurrentExecutablePath(); 83 | auto assetPath = DirUtil::getAssetPath(exePath, "html"); 84 | setupResourceManagerDirectoryProvider(m_resourceManager, URI_ROOT, assetPath); 85 | } 86 | 87 | { 88 | CefWindowInfo windowInfo; 89 | windowInfo.SetAsWindowless(0); 90 | windowInfo.shared_texture_enabled = false; 91 | 92 | CefBrowserSettings browserSettings; 93 | m_browser = CefBrowserHost::CreateBrowserSync(windowInfo, this, URL, browserSettings, nullptr, nullptr); 94 | } 95 | 96 | { 97 | CefVertex vertices[] = { 98 | {{-1, 1}, {0, 0}}, 99 | {{1, 1}, {1, 0}}, 100 | {{1, -1}, {1, 1}}, 101 | {{-1, -1}, {0, 1}}, 102 | }; 103 | 104 | sg_buffer_desc vbuf = { }; 105 | vbuf.usage = SG_USAGE_IMMUTABLE; 106 | vbuf.size = sizeof(vertices); 107 | vbuf.content = vertices; 108 | m_bindings.vertex_buffers[0] = sg_make_buffer(&vbuf); 109 | 110 | uint16_t indices[] = { 111 | 1, 2, 0, 112 | 0, 2, 3 113 | }; 114 | 115 | sg_buffer_desc ibuf = { }; 116 | ibuf.type = SG_BUFFERTYPE_INDEXBUFFER; 117 | ibuf.usage = SG_USAGE_IMMUTABLE; 118 | ibuf.size = sizeof(indices); 119 | ibuf.content = indices; 120 | m_bindings.index_buffer = sg_make_buffer(&ibuf); 121 | } 122 | 123 | { 124 | sg_shader_desc desc = { }; 125 | desc.attrs[0].name = "position"; 126 | desc.attrs[1].name = "texcoord"; 127 | desc.fs.images[0].name = "tex"; 128 | desc.fs.images[0].type = SG_IMAGETYPE_2D; 129 | desc.fs.source = cef_fs_src; 130 | desc.vs.source = cef_vs_src; 131 | m_shader = sg_make_shader(&desc); 132 | } 133 | 134 | { 135 | sg_pipeline_desc desc = { }; 136 | desc.layout.buffers[0].stride = sizeof(CefVertex); 137 | auto& attrs = desc.layout.attrs; 138 | attrs[0].offset = offsetof(CefVertex, pos); attrs[0].format = SG_VERTEXFORMAT_FLOAT2; 139 | attrs[1].offset = offsetof(CefVertex, uv); attrs[1].format = SG_VERTEXFORMAT_FLOAT2; 140 | desc.shader = m_shader; 141 | desc.index_type = SG_INDEXTYPE_UINT16; 142 | desc.blend.enabled = true; 143 | desc.blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA; 144 | desc.blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; 145 | desc.blend.color_write_mask = SG_COLORMASK_RGB; 146 | m_pipeline = sg_make_pipeline(&desc); 147 | } 148 | } 149 | 150 | ~CefGUI() 151 | { 152 | } 153 | 154 | void update() override 155 | { 156 | CefDoMessageLoopWork(); 157 | } 158 | 159 | void draw() override 160 | { 161 | if (m_cefTexture.id == 0) return; 162 | 163 | if (m_imgDirty) 164 | { 165 | // bgra to rgba 166 | for (size_t i=0; i cef_mouse_button_type_t 188 | { 189 | switch (b) { 190 | case SAPP_MOUSEBUTTON_LEFT: return MBT_LEFT; 191 | case SAPP_MOUSEBUTTON_RIGHT: return MBT_RIGHT; 192 | case SAPP_MOUSEBUTTON_MIDDLE: return MBT_MIDDLE; 193 | default: return MBT_RIGHT; 194 | } 195 | }; 196 | 197 | auto host = m_browser->GetHost(); 198 | switch (e.type) 199 | { 200 | case SAPP_EVENTTYPE_MOUSE_MOVE: 201 | { 202 | CefMouseEvent cef; 203 | cef.x = int(e.mouse_x); 204 | cef.y = int(e.mouse_y); 205 | host->SendMouseMoveEvent(cef, false); 206 | } 207 | break; 208 | case SAPP_EVENTTYPE_MOUSE_DOWN: 209 | { 210 | CefMouseEvent cef; 211 | cef.x = int(e.mouse_x); 212 | cef.y = int(e.mouse_y); 213 | host->SendMouseClickEvent(cef, mouseButtonToCef(e.mouse_button), false, 1); 214 | } 215 | break; 216 | case SAPP_EVENTTYPE_MOUSE_UP: 217 | { 218 | CefMouseEvent cef; 219 | cef.x = int(e.mouse_x); 220 | cef.y = int(e.mouse_y); 221 | host->SendMouseClickEvent(cef, mouseButtonToCef(e.mouse_button), true, 1); 222 | } 223 | break; 224 | case SAPP_EVENTTYPE_RESIZED: 225 | host->WasResized(); 226 | break; 227 | default:; 228 | } 229 | } 230 | 231 | void shutdown() override 232 | { 233 | 234 | sg_destroy_pipeline(m_pipeline); 235 | sg_destroy_shader(m_shader); 236 | sg_destroy_buffer(m_bindings.vertex_buffers[0]); 237 | sg_destroy_buffer(m_bindings.index_buffer); 238 | if (m_cefTexture.id) 239 | { 240 | sg_destroy_image(m_cefTexture); 241 | } 242 | 243 | m_browser = nullptr; 244 | } 245 | 246 | void updateRevolutions(const int* rev) override 247 | { 248 | std::ostringstream sout; 249 | sout << "{"; 250 | sout << R"json("x":)json" << rev[0] << ","; 251 | sout << R"json("y":)json" << rev[1] << ","; 252 | sout << R"json("z":)json" << rev[2]; 253 | sout << "}"; 254 | 255 | auto msg = CefProcessMessage::Create("msgFromApp"); 256 | msg->GetArgumentList()->SetString(0, sout.str()); 257 | m_browser->GetMainFrame()->SendProcessMessage(PID_RENDERER, msg); 258 | } 259 | 260 | void updateTextureFromCef(const uint8_t* buffer, int width, int height) 261 | { 262 | // there can be multiple updates per frame from cef, so we collect our 263 | // img data and mark the image as dirty 264 | if (m_imgWidth != width || m_imgHeight != height) 265 | { 266 | if (m_cefTexture.id) 267 | { 268 | sg_destroy_image(m_cefTexture); 269 | } 270 | 271 | m_imgBuffer.resize(width * height * 4); 272 | 273 | sg_image_desc desc = {}; 274 | desc.width = width; 275 | desc.height = height; 276 | desc.usage = SG_USAGE_DYNAMIC; 277 | desc.pixel_format = SG_PIXELFORMAT_RGBA8; 278 | desc.wrap_u = SG_WRAP_CLAMP_TO_EDGE; 279 | desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE; 280 | 281 | m_imgWidth = width; 282 | m_imgHeight = height; 283 | 284 | m_cefTexture = sg_make_image(&desc); 285 | m_bindings.fs_images[0] = m_cefTexture; 286 | } 287 | 288 | memcpy(m_imgBuffer.data(), buffer, m_imgBuffer.size()); 289 | 290 | m_imgDirty = true; 291 | } 292 | 293 | ///////////////////////////////////// 294 | // CEF 295 | CefRefPtr GetRequestHandler() override { return this; } 296 | CefRefPtr GetRenderHandler() override { return this; } 297 | 298 | bool OnProcessMessageReceived(CefRefPtr /*browser*/, CefRefPtr /*frame*/, 299 | CefProcessId /*source_process*/, CefRefPtr message) override 300 | { 301 | if (message->GetName() == "msgFromGUI") 302 | { 303 | auto msg = message->GetArgumentList()->GetString(0).ToString(); 304 | switch(msg[0]) 305 | { 306 | case 'a': 307 | m_rotationAxis = RotationAxis(atoi(msg.c_str() + 2)); 308 | break; 309 | case 'r': 310 | m_rotating = msg[2] == 't'; 311 | break; 312 | case 's': 313 | m_rotationSpeed = float(atof(msg.c_str() + 2)); 314 | break; 315 | } 316 | return true; 317 | } 318 | return false; 319 | } 320 | 321 | ///////////////////////////////////// 322 | // request handler 323 | CefRefPtr GetResourceRequestHandler(CefRefPtr /*browser*/, 324 | CefRefPtr /*frame*/, CefRefPtr /*request*/, bool /*is_navigation*/, 325 | bool /*is_download*/, const CefString& /*request_initiator*/, bool& /*disable_default_handling*/) override { 326 | return this; 327 | } 328 | 329 | ///////////////////////////////////// 330 | // resource request handler 331 | cef_return_value_t OnBeforeResourceLoad(CefRefPtr browser, 332 | CefRefPtr frame, CefRefPtr request, CefRefPtr callback) override 333 | { 334 | return m_resourceManager->OnBeforeResourceLoad(browser, frame, request, callback); 335 | } 336 | 337 | CefRefPtr GetResourceHandler(CefRefPtr browser, 338 | CefRefPtr frame, CefRefPtr request) override 339 | { 340 | return m_resourceManager->GetResourceHandler(browser, frame, request); 341 | } 342 | 343 | ///////////////////////////////////// 344 | // render handler 345 | void GetViewRect(CefRefPtr /*browser*/, CefRect &rect) override 346 | { 347 | rect = CefRect(0, 0, sapp_width(), sapp_height()); 348 | } 349 | 350 | void OnPaint(CefRefPtr /*browser*/, PaintElementType /*type*/, 351 | const RectList& /*dirtyRects*/, const void* buffer, int width, int height) override 352 | { 353 | updateTextureFromCef(reinterpret_cast(buffer), width, height); 354 | } 355 | 356 | private: 357 | CefRefPtr m_browser; 358 | CefRefPtr m_resourceManager; 359 | 360 | sg_pipeline m_pipeline = {}; 361 | sg_bindings m_bindings = {}; 362 | int m_imgWidth = 0, m_imgHeight = 0; 363 | std::vector m_imgBuffer; 364 | bool m_imgDirty = true; 365 | sg_image m_cefTexture = {}; 366 | sg_shader m_shader = {}; 367 | 368 | IMPLEMENT_REFCOUNTING(CefGUI); 369 | DISALLOW_COPY_AND_ASSIGN(CefGUI); 370 | }; 371 | 372 | } 373 | 374 | demo::GUI* DemoCefApp::createGUI() 375 | { 376 | return new CefGUI; 377 | } -------------------------------------------------------------------------------- /demo-3d/sokol-demo/demo/App.cpp: -------------------------------------------------------------------------------- 1 | // HTML 5 GUI Demo 2 | // Copyright (c) 2019 Borislav Stanimirov 3 | // 4 | // Distributed under the MIT Software License 5 | // See accompanying file LICENSE.txt or copy at 6 | // https://opensource.org/licenses/MIT 7 | // 8 | #include "App.hpp" 9 | 10 | #include "GUI.hpp" 11 | 12 | #define SOKOL_NO_DEPRECATED 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | constexpr float PI = 3.14159265f; 20 | 21 | namespace demo 22 | { 23 | 24 | App::App() = default; 25 | App::~App() = default; 26 | 27 | namespace 28 | { 29 | struct Vertex 30 | { 31 | vec3 pos; 32 | vec3 color; 33 | }; 34 | struct matrix 35 | { 36 | float m00, m10, m20, m30; 37 | float m01, m11, m21, m31; 38 | float m02, m12, m22, m32; 39 | float m03, m13, m23, m33; 40 | 41 | static constexpr matrix columns( 42 | float cr00, float cr01, float cr02, float cr03, //column 0 43 | float cr10, float cr11, float cr12, float cr13, //column 1 44 | float cr20, float cr21, float cr22, float cr23, //column 2 45 | float cr30, float cr31, float cr32, float cr33 //column 3 46 | ) 47 | { 48 | return{ 49 | cr00, cr01, cr02, cr03, 50 | cr10, cr11, cr12, cr13, 51 | cr20, cr21, cr22, cr23, 52 | cr30, cr31, cr32, cr33 53 | }; 54 | } 55 | 56 | static constexpr matrix rows( 57 | float rc00, float rc01, float rc02, float rc03, //row 0 58 | float rc10, float rc11, float rc12, float rc13, //row 1 59 | float rc20, float rc21, float rc22, float rc23, //row 2 60 | float rc30, float rc31, float rc32, float rc33 //row 3 61 | ) 62 | { 63 | return{ 64 | rc00, rc10, rc20, rc30, 65 | rc01, rc11, rc21, rc31, 66 | rc02, rc12, rc22, rc32, 67 | rc03, rc13, rc23, rc33, 68 | }; 69 | } 70 | 71 | static constexpr matrix identity() 72 | { 73 | return columns( 74 | 1, 0, 0, 0, 75 | 0, 1, 0, 0, 76 | 0, 0, 1, 0, 77 | 0, 0, 0, 1 78 | ); 79 | } 80 | 81 | static constexpr matrix translation(float x, float y, float z) 82 | { 83 | return rows( 84 | 1, 0, 0, x, 85 | 0, 1, 0, y, 86 | 0, 0, 1, z, 87 | 0, 0, 0, 1 88 | ); 89 | } 90 | 91 | static matrix ortho_rh(float width, float height, float near_dist, float far_dist) 92 | { 93 | auto d = far_dist - near_dist; 94 | return rows( 95 | 2/width, 0, 0, 0, 96 | 0, 2/height, 0, 0, 97 | 0, 0, -1/d, -near_dist/d, 98 | 0, 0, 0, 1 99 | ); 100 | } 101 | 102 | static matrix perspective_rh(float width, float height, float near_dist, float far_dist) 103 | { 104 | auto d = far_dist - near_dist; 105 | return rows( 106 | (2*near_dist)/width, 0, 0, 0, 107 | 0, (2*near_dist)/height, 0, 0, 108 | 0, 0, -far_dist/d, -(far_dist*near_dist)/d, 109 | 0, 0, -1, 0 110 | ); 111 | } 112 | 113 | static matrix rotation_x(float radians) 114 | { 115 | const auto c = std::cos(radians); 116 | const auto s = std::sin(radians); 117 | 118 | return rows( 119 | 1, 0, 0, 0, 120 | 0, c, -s, 0, 121 | 0, s, c, 0, 122 | 0, 0, 0, 1 123 | ); 124 | } 125 | 126 | static matrix rotation_y(float radians) 127 | { 128 | const auto c = std::cos(radians); 129 | const auto s = std::sin(radians); 130 | 131 | return rows( 132 | c, 0, s, 0, 133 | 0, 1, 0, 0, 134 | -s, 0, c, 0, 135 | 0, 0, 0, 1 136 | ); 137 | } 138 | 139 | static matrix rotation_z(float radians) 140 | { 141 | const auto c = std::cos(radians); 142 | const auto s = std::sin(radians); 143 | 144 | return rows( 145 | c, -s, 0, 0, 146 | s, c, 0, 0, 147 | 0, 0, 1, 0, 148 | 0, 0, 0, 1 149 | ); 150 | } 151 | }; 152 | 153 | matrix operator*(const matrix& a, const matrix& b) 154 | { 155 | return matrix::columns( 156 | a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20 + a.m03 * b.m30, 157 | a.m10 * b.m00 + a.m11 * b.m10 + a.m12 * b.m20 + a.m13 * b.m30, 158 | a.m20 * b.m00 + a.m21 * b.m10 + a.m22 * b.m20 + a.m23 * b.m30, 159 | a.m30 * b.m00 + a.m31 * b.m10 + a.m32 * b.m20 + a.m33 * b.m30, 160 | a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21 + a.m03 * b.m31, 161 | a.m10 * b.m01 + a.m11 * b.m11 + a.m12 * b.m21 + a.m13 * b.m31, 162 | a.m20 * b.m01 + a.m21 * b.m11 + a.m22 * b.m21 + a.m23 * b.m31, 163 | a.m30 * b.m01 + a.m31 * b.m11 + a.m32 * b.m21 + a.m33 * b.m31, 164 | a.m00 * b.m02 + a.m01 * b.m12 + a.m02 * b.m22 + a.m03 * b.m32, 165 | a.m10 * b.m02 + a.m11 * b.m12 + a.m12 * b.m22 + a.m13 * b.m32, 166 | a.m20 * b.m02 + a.m21 * b.m12 + a.m22 * b.m22 + a.m23 * b.m32, 167 | a.m30 * b.m02 + a.m31 * b.m12 + a.m32 * b.m22 + a.m33 * b.m32, 168 | a.m00 * b.m03 + a.m01 * b.m13 + a.m02 * b.m23 + a.m03 * b.m33, 169 | a.m10 * b.m03 + a.m11 * b.m13 + a.m12 * b.m23 + a.m13 * b.m33, 170 | a.m20 * b.m03 + a.m21 * b.m13 + a.m22 * b.m23 + a.m23 * b.m33, 171 | a.m30 * b.m03 + a.m31 * b.m13 + a.m32 * b.m23 + a.m33 * b.m33 172 | ); 173 | } 174 | 175 | 176 | const char* vs_src = R"glsl( 177 | #version 330 178 | uniform mat4 u_projView; 179 | uniform mat4 u_model; 180 | in vec4 a_position; 181 | in vec3 a_color; 182 | out vec3 v_color; 183 | void main() { 184 | v_color = a_color; 185 | gl_Position = u_projView * u_model * a_position; 186 | }; 187 | )glsl"; 188 | 189 | const char* fs_src = R"glsl( 190 | #version 330 191 | uniform sampler2D tex; 192 | in vec3 v_color; 193 | out vec4 frag_color; 194 | void main() { 195 | frag_color = vec4(v_color, 1); 196 | }; 197 | )glsl"; 198 | 199 | struct UniformBlock 200 | { 201 | matrix projView; 202 | matrix model; 203 | }; 204 | 205 | } 206 | 207 | class RotatingCube 208 | { 209 | public: 210 | RotatingCube() 211 | { 212 | { 213 | Vertex vertices[] = { 214 | //bottom 215 | {{-1,-1,-1},{1, 0, 0}}, {{ 1, 1,-1},{0, 1, 0}}, {{ 1,-1,-1},{0, 0, 1}}, {{-1, 1,-1},{1, 1, 1}}, 216 | // top 217 | {{-1,-1, 1},{1, 0, 0}}, {{ 1,-1, 1},{0, 1, 0}}, {{ 1, 1, 1},{0, 0, 1}}, {{-1, 1, 1},{1, 1, 1}}, 218 | //south 219 | {{-1,-1,-1},{1, 0, 0}}, {{ 1,-1,-1},{0, 1, 0}}, {{-1,-1, 1},{0, 0, 1}}, {{ 1,-1, 1},{1, 1, 1}}, 220 | // north 221 | {{-1, 1,-1},{1, 0, 0}}, {{-1, 1, 1},{0, 1, 0}}, {{ 1, 1,-1},{0, 0, 1}}, {{ 1, 1, 1},{1, 1, 1}}, 222 | //east 223 | {{ 1,-1,-1},{1, 0, 0}}, {{ 1, 1, 1},{0, 1, 0}}, {{ 1,-1, 1},{0, 0, 1}}, {{ 1, 1,-1},{1, 1, 1}}, 224 | // west 225 | {{-1,-1,-1},{1, 0, 0}}, {{-1,-1, 1},{0, 1, 0}}, {{-1, 1, 1},{0, 0, 1}}, {{-1, 1,-1},{1, 1, 1}}, 226 | }; 227 | sg_buffer_desc vbuf = { }; 228 | vbuf.usage = SG_USAGE_IMMUTABLE; 229 | vbuf.size = sizeof(vertices); 230 | vbuf.content = vertices; 231 | m_bindings.vertex_buffers[0] = sg_make_buffer(&vbuf); 232 | 233 | uint16_t indices[] = { 234 | // bottom 235 | 0, 1, 2, 1, 0, 3, 236 | // top 237 | 4, 5, 6, 6, 7, 4, 238 | // south 239 | 8, 9, 10, 9, 11, 10, 240 | // north 241 | 12, 13, 14, 14, 13, 15, 242 | //east 243 | 16, 17, 18, 17, 16, 19, 244 | // west 245 | 20, 21, 22, 22, 23, 20, 246 | }; 247 | 248 | sg_buffer_desc ibuf = { }; 249 | ibuf.type = SG_BUFFERTYPE_INDEXBUFFER; 250 | ibuf.usage = SG_USAGE_IMMUTABLE; 251 | ibuf.size = sizeof(indices); 252 | ibuf.content = indices; 253 | m_bindings.index_buffer = sg_make_buffer(&ibuf); 254 | } 255 | 256 | { 257 | sg_shader_desc desc = { }; 258 | desc.attrs[0].name = "a_position"; 259 | desc.attrs[1].name = "a_color"; 260 | 261 | desc.vs.uniform_blocks[0].size = sizeof(UniformBlock); 262 | desc.vs.uniform_blocks[0].uniforms[0].name = "u_projView"; 263 | desc.vs.uniform_blocks[0].uniforms[0].type = SG_UNIFORMTYPE_MAT4; 264 | desc.vs.uniform_blocks[0].uniforms[1].name = "u_model"; 265 | desc.vs.uniform_blocks[0].uniforms[1].type = SG_UNIFORMTYPE_MAT4; 266 | 267 | desc.fs.source = fs_src; 268 | desc.vs.source = vs_src; 269 | m_shader = sg_make_shader(&desc); 270 | } 271 | 272 | { 273 | sg_pipeline_desc desc = { }; 274 | desc.layout.buffers[0].stride = sizeof(Vertex); 275 | auto& attrs = desc.layout.attrs; 276 | attrs[0].offset = offsetof(Vertex, pos); attrs[0].format = SG_VERTEXFORMAT_FLOAT3; 277 | attrs[1].offset = offsetof(Vertex, color); attrs[1].format = SG_VERTEXFORMAT_FLOAT3; 278 | 279 | desc.shader = m_shader; 280 | desc.index_type = SG_INDEXTYPE_UINT16; 281 | desc.blend.enabled = false; 282 | 283 | desc.depth_stencil.depth_compare_func = SG_COMPAREFUNC_LESS_EQUAL; 284 | desc.depth_stencil.depth_write_enabled = true; 285 | 286 | m_pipeline = sg_make_pipeline(&desc); 287 | } 288 | } 289 | 290 | ~RotatingCube() 291 | { 292 | sg_destroy_pipeline(m_pipeline); 293 | sg_destroy_shader(m_shader); 294 | sg_destroy_buffer(m_bindings.vertex_buffers[0]); 295 | sg_destroy_buffer(m_bindings.index_buffer); 296 | } 297 | 298 | void draw(vec3 rotation) // dt = delta from last update in seconds 299 | { 300 | sg_apply_pipeline(m_pipeline); 301 | sg_apply_bindings(&m_bindings); 302 | 303 | auto a = float(sapp_width()) / sapp_height(); 304 | UniformBlock block = { 305 | matrix::perspective_rh(a, 1, 1, 1000) * matrix::translation(0, 0, -5), 306 | //matrix::ortho_rh(4 * a, 4, 5, -5), 307 | matrix::rotation_z(rotation.c[2]) * matrix::rotation_y(rotation.c[1]) * matrix::rotation_x(rotation.c[0]) 308 | }; 309 | sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &block, sizeof(UniformBlock)); 310 | sg_draw(0, 36, 1); 311 | } 312 | 313 | sg_pipeline m_pipeline = {}; 314 | sg_bindings m_bindings = {}; 315 | sg_shader m_shader = {}; 316 | }; 317 | 318 | void App::init() 319 | { 320 | m_currentFrameTime = App::getTicks(); 321 | 322 | sg_desc desc = {}; 323 | desc.mtl_device = sapp_metal_get_device(); 324 | desc.mtl_renderpass_descriptor_cb = sapp_metal_get_renderpass_descriptor; 325 | desc.mtl_drawable_cb = sapp_metal_get_drawable; 326 | desc.d3d11_device = sapp_d3d11_get_device(); 327 | desc.d3d11_device_context = sapp_d3d11_get_device_context(); 328 | desc.d3d11_render_target_view_cb = sapp_d3d11_get_render_target_view; 329 | desc.d3d11_depth_stencil_view_cb = sapp_d3d11_get_depth_stencil_view; 330 | desc.gl_force_gles2 = sapp_gles2(); 331 | sg_setup(&desc); 332 | 333 | m_cube = std::make_unique(); 334 | m_gui = createGUI(); 335 | } 336 | 337 | void App::mainLoop() 338 | { 339 | uint32_t now = App::getTicks(); 340 | m_timeSinceLastFrame = now - m_currentFrameTime; 341 | m_currentFrameTime = now; 342 | 343 | if (m_gui->rotating()) 344 | { 345 | auto a = m_gui->rotationAxis(); 346 | auto& r = m_rotation.c[a]; 347 | r += (float(m_timeSinceLastFrame) / 1000) * m_gui->rotationSpeed(); 348 | if (r > PI * 2) 349 | { 350 | r -= PI * 2; 351 | ++m_revolutions.c[a]; 352 | m_gui->updateRevolutions(m_revolutions.c); 353 | } 354 | } 355 | 356 | m_gui->update(); 357 | 358 | sg_pass_action passAction = {}; 359 | 360 | passAction.colors[0].action = SG_ACTION_CLEAR; 361 | passAction.colors[0].val[0] = 1; 362 | passAction.colors[0].val[1] = 1; 363 | passAction.colors[0].val[2] = 1; 364 | passAction.colors[0].val[3] = 1; 365 | passAction.depth.action = SG_ACTION_CLEAR; 366 | passAction.depth.val = 1; 367 | passAction.stencil.action = SG_ACTION_DONTCARE; 368 | 369 | const int width = sapp_width(); 370 | const int height = sapp_height(); 371 | sg_begin_default_pass(&passAction, width, height); 372 | 373 | m_cube->draw(m_rotation); 374 | 375 | m_gui->draw(); 376 | 377 | sg_end_pass(); 378 | sg_commit(); 379 | } 380 | 381 | void App::shutdown() 382 | { 383 | m_gui->shutdown(); 384 | m_gui = nullptr; 385 | m_cube.reset(); 386 | sg_shutdown(); 387 | } 388 | 389 | void App::onEvent(const sapp_event& e) 390 | { 391 | m_gui->onEvent(e); 392 | } 393 | 394 | uint32_t App::getTicks() 395 | { 396 | // actually the time returned is since the first time this function is called 397 | // but this is very early in the execution time, so it's fine 398 | static auto start = std::chrono::steady_clock::now(); 399 | auto time = std::chrono::steady_clock::now() - start; 400 | return uint32_t(std::chrono::duration_cast(time).count()) + 1; 401 | } 402 | 403 | } -------------------------------------------------------------------------------- /fs-browser/browser-lib/fsbrowser/win_dirent.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Dirent interface for Microsoft Visual Studio 3 | * 4 | * Copyright (C) 1998-2019 Toni Ronkko 5 | * This file is part of dirent. Dirent may be freely distributed 6 | * under the MIT license. For all details and documentation, see 7 | * https://github.com/tronkko/dirent 8 | */ 9 | #ifndef DIRENT_H 10 | #define DIRENT_H 11 | 12 | /* Hide warnings about unreferenced local functions */ 13 | #if defined(__clang__) 14 | # pragma clang diagnostic ignored "-Wunused-function" 15 | #elif defined(_MSC_VER) 16 | # pragma warning(disable:4505) 17 | #elif defined(__GNUC__) 18 | # pragma GCC diagnostic ignored "-Wunused-function" 19 | #endif 20 | 21 | /* 22 | * Include windows.h without Windows Sockets 1.1 to prevent conflicts with 23 | * Windows Sockets 2.0. 24 | */ 25 | #ifndef WIN32_LEAN_AND_MEAN 26 | # define WIN32_LEAN_AND_MEAN 27 | #endif 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | /* Indicates that d_type field is available in dirent structure */ 41 | #define _DIRENT_HAVE_D_TYPE 42 | 43 | /* Indicates that d_namlen field is available in dirent structure */ 44 | #define _DIRENT_HAVE_D_NAMLEN 45 | 46 | /* Entries missing from MSVC 6.0 */ 47 | #if !defined(FILE_ATTRIBUTE_DEVICE) 48 | # define FILE_ATTRIBUTE_DEVICE 0x40 49 | #endif 50 | 51 | /* File type and permission flags for stat(), general mask */ 52 | #if !defined(S_IFMT) 53 | # define S_IFMT _S_IFMT 54 | #endif 55 | 56 | /* Directory bit */ 57 | #if !defined(S_IFDIR) 58 | # define S_IFDIR _S_IFDIR 59 | #endif 60 | 61 | /* Character device bit */ 62 | #if !defined(S_IFCHR) 63 | # define S_IFCHR _S_IFCHR 64 | #endif 65 | 66 | /* Pipe bit */ 67 | #if !defined(S_IFFIFO) 68 | # define S_IFFIFO _S_IFFIFO 69 | #endif 70 | 71 | /* Regular file bit */ 72 | #if !defined(S_IFREG) 73 | # define S_IFREG _S_IFREG 74 | #endif 75 | 76 | /* Read permission */ 77 | #if !defined(S_IREAD) 78 | # define S_IREAD _S_IREAD 79 | #endif 80 | 81 | /* Write permission */ 82 | #if !defined(S_IWRITE) 83 | # define S_IWRITE _S_IWRITE 84 | #endif 85 | 86 | /* Execute permission */ 87 | #if !defined(S_IEXEC) 88 | # define S_IEXEC _S_IEXEC 89 | #endif 90 | 91 | /* Pipe */ 92 | #if !defined(S_IFIFO) 93 | # define S_IFIFO _S_IFIFO 94 | #endif 95 | 96 | /* Block device */ 97 | #if !defined(S_IFBLK) 98 | # define S_IFBLK 0 99 | #endif 100 | 101 | /* Link */ 102 | #if !defined(S_IFLNK) 103 | # define S_IFLNK 0 104 | #endif 105 | 106 | /* Socket */ 107 | #if !defined(S_IFSOCK) 108 | # define S_IFSOCK 0 109 | #endif 110 | 111 | /* Read user permission */ 112 | #if !defined(S_IRUSR) 113 | # define S_IRUSR S_IREAD 114 | #endif 115 | 116 | /* Write user permission */ 117 | #if !defined(S_IWUSR) 118 | # define S_IWUSR S_IWRITE 119 | #endif 120 | 121 | /* Execute user permission */ 122 | #if !defined(S_IXUSR) 123 | # define S_IXUSR 0 124 | #endif 125 | 126 | /* Read group permission */ 127 | #if !defined(S_IRGRP) 128 | # define S_IRGRP 0 129 | #endif 130 | 131 | /* Write group permission */ 132 | #if !defined(S_IWGRP) 133 | # define S_IWGRP 0 134 | #endif 135 | 136 | /* Execute group permission */ 137 | #if !defined(S_IXGRP) 138 | # define S_IXGRP 0 139 | #endif 140 | 141 | /* Read others permission */ 142 | #if !defined(S_IROTH) 143 | # define S_IROTH 0 144 | #endif 145 | 146 | /* Write others permission */ 147 | #if !defined(S_IWOTH) 148 | # define S_IWOTH 0 149 | #endif 150 | 151 | /* Execute others permission */ 152 | #if !defined(S_IXOTH) 153 | # define S_IXOTH 0 154 | #endif 155 | 156 | /* Maximum length of file name */ 157 | #if !defined(PATH_MAX) 158 | # define PATH_MAX MAX_PATH 159 | #endif 160 | #if !defined(FILENAME_MAX) 161 | # define FILENAME_MAX MAX_PATH 162 | #endif 163 | #if !defined(NAME_MAX) 164 | # define NAME_MAX FILENAME_MAX 165 | #endif 166 | 167 | /* File type flags for d_type */ 168 | #define DT_UNKNOWN 0 169 | #define DT_REG S_IFREG 170 | #define DT_DIR S_IFDIR 171 | #define DT_FIFO S_IFIFO 172 | #define DT_SOCK S_IFSOCK 173 | #define DT_CHR S_IFCHR 174 | #define DT_BLK S_IFBLK 175 | #define DT_LNK S_IFLNK 176 | 177 | /* Macros for converting between st_mode and d_type */ 178 | #define IFTODT(mode) ((mode) & S_IFMT) 179 | #define DTTOIF(type) (type) 180 | 181 | /* 182 | * File type macros. Note that block devices, sockets and links cannot be 183 | * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are 184 | * only defined for compatibility. These macros should always return false 185 | * on Windows. 186 | */ 187 | #if !defined(S_ISFIFO) 188 | # define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) 189 | #endif 190 | #if !defined(S_ISDIR) 191 | # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) 192 | #endif 193 | #if !defined(S_ISREG) 194 | # define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) 195 | #endif 196 | #if !defined(S_ISLNK) 197 | # define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) 198 | #endif 199 | #if !defined(S_ISSOCK) 200 | # define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) 201 | #endif 202 | #if !defined(S_ISCHR) 203 | # define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) 204 | #endif 205 | #if !defined(S_ISBLK) 206 | # define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) 207 | #endif 208 | 209 | /* Return the exact length of the file name without zero terminator */ 210 | #define _D_EXACT_NAMLEN(p) ((p)->d_namlen) 211 | 212 | /* Return the maximum size of a file name */ 213 | #define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) 214 | 215 | 216 | #ifdef __cplusplus 217 | extern "C" { 218 | #endif 219 | 220 | 221 | /* Wide-character version */ 222 | struct _wdirent { 223 | /* Always zero */ 224 | long d_ino; 225 | 226 | /* File position within stream */ 227 | long d_off; 228 | 229 | /* Structure size */ 230 | unsigned short d_reclen; 231 | 232 | /* Length of name without \0 */ 233 | size_t d_namlen; 234 | 235 | /* File type */ 236 | int d_type; 237 | 238 | /* File name */ 239 | wchar_t d_name[PATH_MAX + 1]; 240 | }; 241 | typedef struct _wdirent _wdirent; 242 | 243 | struct _WDIR { 244 | /* Current directory entry */ 245 | struct _wdirent ent; 246 | 247 | /* Private file data */ 248 | WIN32_FIND_DATAW data; 249 | 250 | /* True if data is valid */ 251 | int cached; 252 | 253 | /* Win32 search handle */ 254 | HANDLE handle; 255 | 256 | /* Initial directory name */ 257 | wchar_t *patt; 258 | }; 259 | typedef struct _WDIR _WDIR; 260 | 261 | /* Multi-byte character version */ 262 | struct dirent { 263 | /* Always zero */ 264 | long d_ino; 265 | 266 | /* File position within stream */ 267 | long d_off; 268 | 269 | /* Structure size */ 270 | unsigned short d_reclen; 271 | 272 | /* Length of name without \0 */ 273 | size_t d_namlen; 274 | 275 | /* File type */ 276 | int d_type; 277 | 278 | /* File name */ 279 | char d_name[PATH_MAX + 1]; 280 | }; 281 | typedef struct dirent dirent; 282 | 283 | struct DIR { 284 | struct dirent ent; 285 | struct _WDIR *wdirp; 286 | }; 287 | typedef struct DIR DIR; 288 | 289 | 290 | /* Dirent functions */ 291 | static DIR *opendir(const char *dirname); 292 | static _WDIR *_wopendir(const wchar_t *dirname); 293 | 294 | static struct dirent *readdir(DIR *dirp); 295 | static struct _wdirent *_wreaddir(_WDIR *dirp); 296 | 297 | static int readdir_r( 298 | DIR *dirp, struct dirent *entry, struct dirent **result); 299 | static int _wreaddir_r( 300 | _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); 301 | 302 | static int closedir(DIR *dirp); 303 | static int _wclosedir(_WDIR *dirp); 304 | 305 | static void rewinddir(DIR* dirp); 306 | static void _wrewinddir(_WDIR* dirp); 307 | 308 | static int scandir(const char *dirname, struct dirent ***namelist, 309 | int(*filter)(const struct dirent*), 310 | int(*compare)(const struct dirent**, const struct dirent**)); 311 | 312 | static int alphasort(const struct dirent **a, const struct dirent **b); 313 | 314 | static int versionsort(const struct dirent **a, const struct dirent **b); 315 | 316 | 317 | /* For compatibility with Symbian */ 318 | #define wdirent _wdirent 319 | #define WDIR _WDIR 320 | #define wopendir _wopendir 321 | #define wreaddir _wreaddir 322 | #define wclosedir _wclosedir 323 | #define wrewinddir _wrewinddir 324 | 325 | 326 | /* Internal utility functions */ 327 | static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp); 328 | static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp); 329 | 330 | static int dirent_mbstowcs_s( 331 | size_t *pReturnValue, 332 | wchar_t *wcstr, 333 | size_t sizeInWords, 334 | const char *mbstr, 335 | size_t count); 336 | 337 | static int dirent_wcstombs_s( 338 | size_t *pReturnValue, 339 | char *mbstr, 340 | size_t sizeInBytes, 341 | const wchar_t *wcstr, 342 | size_t count); 343 | 344 | static void dirent_set_errno(int error); 345 | 346 | 347 | /* 348 | * Open directory stream DIRNAME for read and return a pointer to the 349 | * internal working area that is used to retrieve individual directory 350 | * entries. 351 | */ 352 | static _WDIR* 353 | _wopendir( 354 | const wchar_t *dirname) 355 | { 356 | _WDIR *dirp; 357 | DWORD n; 358 | wchar_t *p; 359 | 360 | /* Must have directory name */ 361 | if (dirname == NULL || dirname[0] == '\0') { 362 | dirent_set_errno(ENOENT); 363 | return NULL; 364 | } 365 | 366 | /* Allocate new _WDIR structure */ 367 | dirp = (_WDIR*)malloc(sizeof(struct _WDIR)); 368 | if (!dirp) { 369 | return NULL; 370 | } 371 | 372 | /* Reset _WDIR structure */ 373 | dirp->handle = INVALID_HANDLE_VALUE; 374 | dirp->patt = NULL; 375 | dirp->cached = 0; 376 | 377 | /* 378 | * Compute the length of full path plus zero terminator 379 | * 380 | * Note that on WinRT there's no way to convert relative paths 381 | * into absolute paths, so just assume it is an absolute path. 382 | */ 383 | #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) 384 | /* Desktop */ 385 | n = GetFullPathNameW(dirname, 0, NULL, NULL); 386 | #else 387 | /* WinRT */ 388 | n = wcslen(dirname); 389 | #endif 390 | 391 | /* Allocate room for absolute directory name and search pattern */ 392 | dirp->patt = (wchar_t*)malloc(sizeof(wchar_t) * n + 16); 393 | if (dirp->patt == NULL) { 394 | goto exit_closedir; 395 | } 396 | 397 | /* 398 | * Convert relative directory name to an absolute one. This 399 | * allows rewinddir() to function correctly even when current 400 | * working directory is changed between opendir() and rewinddir(). 401 | * 402 | * Note that on WinRT there's no way to convert relative paths 403 | * into absolute paths, so just assume it is an absolute path. 404 | */ 405 | #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) 406 | /* Desktop */ 407 | n = GetFullPathNameW(dirname, n, dirp->patt, NULL); 408 | if (n <= 0) { 409 | goto exit_closedir; 410 | } 411 | #else 412 | /* WinRT */ 413 | wcsncpy_s(dirp->patt, n + 1, dirname, n); 414 | #endif 415 | 416 | /* Append search pattern \* to the directory name */ 417 | p = dirp->patt + n; 418 | switch (p[-1]) { 419 | case '\\': 420 | case '/': 421 | case ':': 422 | /* Directory ends in path separator, e.g. c:\temp\ */ 423 | /*NOP*/; 424 | break; 425 | 426 | default: 427 | /* Directory name doesn't end in path separator */ 428 | *p++ = '\\'; 429 | } 430 | *p++ = '*'; 431 | *p = '\0'; 432 | 433 | /* Open directory stream and retrieve the first entry */ 434 | if (!dirent_first(dirp)) { 435 | goto exit_closedir; 436 | } 437 | 438 | /* Success */ 439 | return dirp; 440 | 441 | /* Failure */ 442 | exit_closedir: 443 | _wclosedir(dirp); 444 | return NULL; 445 | } 446 | 447 | /* 448 | * Read next directory entry. 449 | * 450 | * Returns pointer to static directory entry which may be overwritten by 451 | * subsequent calls to _wreaddir(). 452 | */ 453 | static struct _wdirent* 454 | _wreaddir( 455 | _WDIR *dirp) 456 | { 457 | struct _wdirent *entry; 458 | 459 | /* 460 | * Read directory entry to buffer. We can safely ignore the return value 461 | * as entry will be set to NULL in case of error. 462 | */ 463 | (void)_wreaddir_r(dirp, &dirp->ent, &entry); 464 | 465 | /* Return pointer to statically allocated directory entry */ 466 | return entry; 467 | } 468 | 469 | /* 470 | * Read next directory entry. 471 | * 472 | * Returns zero on success. If end of directory stream is reached, then sets 473 | * result to NULL and returns zero. 474 | */ 475 | static int 476 | _wreaddir_r( 477 | _WDIR *dirp, 478 | struct _wdirent *entry, 479 | struct _wdirent **result) 480 | { 481 | WIN32_FIND_DATAW *datap; 482 | 483 | /* Read next directory entry */ 484 | datap = dirent_next(dirp); 485 | if (datap) { 486 | size_t n; 487 | DWORD attr; 488 | 489 | /* 490 | * Copy file name as wide-character string. If the file name is too 491 | * long to fit in to the destination buffer, then truncate file name 492 | * to PATH_MAX characters and zero-terminate the buffer. 493 | */ 494 | n = 0; 495 | while (n < PATH_MAX && datap->cFileName[n] != 0) { 496 | entry->d_name[n] = datap->cFileName[n]; 497 | n++; 498 | } 499 | entry->d_name[n] = 0; 500 | 501 | /* Length of file name excluding zero terminator */ 502 | entry->d_namlen = n; 503 | 504 | /* File type */ 505 | attr = datap->dwFileAttributes; 506 | if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { 507 | entry->d_type = DT_CHR; 508 | } 509 | else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { 510 | entry->d_type = DT_DIR; 511 | } 512 | else { 513 | entry->d_type = DT_REG; 514 | } 515 | 516 | /* Reset dummy fields */ 517 | entry->d_ino = 0; 518 | entry->d_off = 0; 519 | entry->d_reclen = sizeof(struct _wdirent); 520 | 521 | /* Set result address */ 522 | *result = entry; 523 | 524 | } 525 | else { 526 | 527 | /* Return NULL to indicate end of directory */ 528 | *result = NULL; 529 | 530 | } 531 | 532 | return /*OK*/0; 533 | } 534 | 535 | /* 536 | * Close directory stream opened by opendir() function. This invalidates the 537 | * DIR structure as well as any directory entry read previously by 538 | * _wreaddir(). 539 | */ 540 | static int 541 | _wclosedir( 542 | _WDIR *dirp) 543 | { 544 | int ok; 545 | if (dirp) { 546 | 547 | /* Release search handle */ 548 | if (dirp->handle != INVALID_HANDLE_VALUE) { 549 | FindClose(dirp->handle); 550 | } 551 | 552 | /* Release search pattern */ 553 | free(dirp->patt); 554 | 555 | /* Release directory structure */ 556 | free(dirp); 557 | ok = /*success*/0; 558 | 559 | } 560 | else { 561 | 562 | /* Invalid directory stream */ 563 | dirent_set_errno(EBADF); 564 | ok = /*failure*/-1; 565 | 566 | } 567 | return ok; 568 | } 569 | 570 | /* 571 | * Rewind directory stream such that _wreaddir() returns the very first 572 | * file name again. 573 | */ 574 | static void 575 | _wrewinddir( 576 | _WDIR* dirp) 577 | { 578 | if (dirp) { 579 | /* Release existing search handle */ 580 | if (dirp->handle != INVALID_HANDLE_VALUE) { 581 | FindClose(dirp->handle); 582 | } 583 | 584 | /* Open new search handle */ 585 | dirent_first(dirp); 586 | } 587 | } 588 | 589 | /* Get first directory entry (internal) */ 590 | static WIN32_FIND_DATAW* 591 | dirent_first( 592 | _WDIR *dirp) 593 | { 594 | WIN32_FIND_DATAW *datap; 595 | DWORD error; 596 | 597 | /* Open directory and retrieve the first entry */ 598 | dirp->handle = FindFirstFileExW( 599 | dirp->patt, FindExInfoStandard, &dirp->data, 600 | FindExSearchNameMatch, NULL, 0); 601 | if (dirp->handle != INVALID_HANDLE_VALUE) { 602 | 603 | /* a directory entry is now waiting in memory */ 604 | datap = &dirp->data; 605 | dirp->cached = 1; 606 | 607 | } 608 | else { 609 | 610 | /* Failed to open directory: no directory entry in memory */ 611 | dirp->cached = 0; 612 | datap = NULL; 613 | 614 | /* Set error code */ 615 | error = GetLastError(); 616 | switch (error) { 617 | case ERROR_ACCESS_DENIED: 618 | /* No read access to directory */ 619 | dirent_set_errno(EACCES); 620 | break; 621 | 622 | case ERROR_DIRECTORY: 623 | /* Directory name is invalid */ 624 | dirent_set_errno(ENOTDIR); 625 | break; 626 | 627 | case ERROR_PATH_NOT_FOUND: 628 | default: 629 | /* Cannot find the file */ 630 | dirent_set_errno(ENOENT); 631 | } 632 | 633 | } 634 | return datap; 635 | } 636 | 637 | /* 638 | * Get next directory entry (internal). 639 | * 640 | * Returns 641 | */ 642 | static WIN32_FIND_DATAW* 643 | dirent_next( 644 | _WDIR *dirp) 645 | { 646 | WIN32_FIND_DATAW *p; 647 | 648 | /* Get next directory entry */ 649 | if (dirp->cached != 0) { 650 | 651 | /* A valid directory entry already in memory */ 652 | p = &dirp->data; 653 | dirp->cached = 0; 654 | 655 | } 656 | else if (dirp->handle != INVALID_HANDLE_VALUE) { 657 | 658 | /* Get the next directory entry from stream */ 659 | if (FindNextFileW(dirp->handle, &dirp->data) != FALSE) { 660 | /* Got a file */ 661 | p = &dirp->data; 662 | } 663 | else { 664 | /* The very last entry has been processed or an error occurred */ 665 | FindClose(dirp->handle); 666 | dirp->handle = INVALID_HANDLE_VALUE; 667 | p = NULL; 668 | } 669 | 670 | } 671 | else { 672 | 673 | /* End of directory stream reached */ 674 | p = NULL; 675 | 676 | } 677 | 678 | return p; 679 | } 680 | 681 | /* 682 | * Open directory stream using plain old C-string. 683 | */ 684 | static DIR* 685 | opendir( 686 | const char *dirname) 687 | { 688 | struct DIR *dirp; 689 | 690 | /* Must have directory name */ 691 | if (dirname == NULL || dirname[0] == '\0') { 692 | dirent_set_errno(ENOENT); 693 | return NULL; 694 | } 695 | 696 | /* Allocate memory for DIR structure */ 697 | dirp = (DIR*)malloc(sizeof(struct DIR)); 698 | if (!dirp) { 699 | return NULL; 700 | } 701 | { 702 | int error; 703 | wchar_t wname[PATH_MAX + 1]; 704 | size_t n; 705 | 706 | /* Convert directory name to wide-character string */ 707 | error = dirent_mbstowcs_s( 708 | &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); 709 | if (error) { 710 | /* 711 | * Cannot convert file name to wide-character string. This 712 | * occurs if the string contains invalid multi-byte sequences or 713 | * the output buffer is too small to contain the resulting 714 | * string. 715 | */ 716 | goto exit_free; 717 | } 718 | 719 | 720 | /* Open directory stream using wide-character name */ 721 | dirp->wdirp = _wopendir(wname); 722 | if (!dirp->wdirp) { 723 | goto exit_free; 724 | } 725 | 726 | } 727 | 728 | /* Success */ 729 | return dirp; 730 | 731 | /* Failure */ 732 | exit_free: 733 | free(dirp); 734 | return NULL; 735 | } 736 | 737 | /* 738 | * Read next directory entry. 739 | */ 740 | static struct dirent* 741 | readdir( 742 | DIR *dirp) 743 | { 744 | struct dirent *entry; 745 | 746 | /* 747 | * Read directory entry to buffer. We can safely ignore the return value 748 | * as entry will be set to NULL in case of error. 749 | */ 750 | (void)readdir_r(dirp, &dirp->ent, &entry); 751 | 752 | /* Return pointer to statically allocated directory entry */ 753 | return entry; 754 | } 755 | 756 | /* 757 | * Read next directory entry into called-allocated buffer. 758 | * 759 | * Returns zero on success. If the end of directory stream is reached, then 760 | * sets result to NULL and returns zero. 761 | */ 762 | static int 763 | readdir_r( 764 | DIR *dirp, 765 | struct dirent *entry, 766 | struct dirent **result) 767 | { 768 | WIN32_FIND_DATAW *datap; 769 | 770 | /* Read next directory entry */ 771 | datap = dirent_next(dirp->wdirp); 772 | if (datap) { 773 | size_t n; 774 | int error; 775 | 776 | /* Attempt to convert file name to multi-byte string */ 777 | error = dirent_wcstombs_s( 778 | &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); 779 | 780 | /* 781 | * If the file name cannot be represented by a multi-byte string, 782 | * then attempt to use old 8+3 file name. This allows traditional 783 | * Unix-code to access some file names despite of unicode 784 | * characters, although file names may seem unfamiliar to the user. 785 | * 786 | * Be ware that the code below cannot come up with a short file 787 | * name unless the file system provides one. At least 788 | * VirtualBox shared folders fail to do this. 789 | */ 790 | if (error && datap->cAlternateFileName[0] != '\0') { 791 | error = dirent_wcstombs_s( 792 | &n, entry->d_name, PATH_MAX + 1, 793 | datap->cAlternateFileName, PATH_MAX + 1); 794 | } 795 | 796 | if (!error) { 797 | DWORD attr; 798 | 799 | /* Length of file name excluding zero terminator */ 800 | entry->d_namlen = n - 1; 801 | 802 | /* File attributes */ 803 | attr = datap->dwFileAttributes; 804 | if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { 805 | entry->d_type = DT_CHR; 806 | } 807 | else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { 808 | entry->d_type = DT_DIR; 809 | } 810 | else { 811 | entry->d_type = DT_REG; 812 | } 813 | 814 | /* Reset dummy fields */ 815 | entry->d_ino = 0; 816 | entry->d_off = 0; 817 | entry->d_reclen = sizeof(struct dirent); 818 | 819 | } 820 | else { 821 | 822 | /* 823 | * Cannot convert file name to multi-byte string so construct 824 | * an erroneous directory entry and return that. Note that 825 | * we cannot return NULL as that would stop the processing 826 | * of directory entries completely. 827 | */ 828 | entry->d_name[0] = '?'; 829 | entry->d_name[1] = '\0'; 830 | entry->d_namlen = 1; 831 | entry->d_type = DT_UNKNOWN; 832 | entry->d_ino = 0; 833 | entry->d_off = -1; 834 | entry->d_reclen = 0; 835 | 836 | } 837 | 838 | /* Return pointer to directory entry */ 839 | *result = entry; 840 | 841 | } 842 | else { 843 | 844 | /* No more directory entries */ 845 | *result = NULL; 846 | 847 | } 848 | 849 | return /*OK*/0; 850 | } 851 | 852 | /* 853 | * Close directory stream. 854 | */ 855 | static int 856 | closedir( 857 | DIR *dirp) 858 | { 859 | int ok; 860 | if (dirp) { 861 | 862 | /* Close wide-character directory stream */ 863 | ok = _wclosedir(dirp->wdirp); 864 | dirp->wdirp = NULL; 865 | 866 | /* Release multi-byte character version */ 867 | free(dirp); 868 | 869 | } 870 | else { 871 | 872 | /* Invalid directory stream */ 873 | dirent_set_errno(EBADF); 874 | ok = /*failure*/-1; 875 | 876 | } 877 | return ok; 878 | } 879 | 880 | /* 881 | * Rewind directory stream to beginning. 882 | */ 883 | static void 884 | rewinddir( 885 | DIR* dirp) 886 | { 887 | /* Rewind wide-character string directory stream */ 888 | _wrewinddir(dirp->wdirp); 889 | } 890 | 891 | /* 892 | * Scan directory for entries. 893 | */ 894 | static int 895 | scandir( 896 | const char *dirname, 897 | struct dirent ***namelist, 898 | int(*filter)(const struct dirent*), 899 | int(*compare)(const struct dirent**, const struct dirent**)) 900 | { 901 | struct dirent **files = NULL; 902 | size_t size = 0; 903 | size_t allocated = 0; 904 | const size_t init_size = 1; 905 | DIR *dir = NULL; 906 | struct dirent *entry; 907 | struct dirent *tmp = NULL; 908 | size_t i; 909 | int result = 0; 910 | 911 | /* Open directory stream */ 912 | dir = opendir(dirname); 913 | if (dir) { 914 | 915 | /* Read directory entries to memory */ 916 | while (1) { 917 | 918 | /* Enlarge pointer table to make room for another pointer */ 919 | if (size >= allocated) { 920 | void *p; 921 | size_t num_entries; 922 | 923 | /* Compute number of entries in the enlarged pointer table */ 924 | if (size < init_size) { 925 | /* Allocate initial pointer table */ 926 | num_entries = init_size; 927 | } 928 | else { 929 | /* Double the size */ 930 | num_entries = size * 2; 931 | } 932 | 933 | /* Allocate first pointer table or enlarge existing table */ 934 | p = realloc(files, sizeof(void*) * num_entries); 935 | if (p != NULL) { 936 | /* Got the memory */ 937 | files = (dirent**)p; 938 | allocated = num_entries; 939 | } 940 | else { 941 | /* Out of memory */ 942 | result = -1; 943 | break; 944 | } 945 | 946 | } 947 | 948 | /* Allocate room for temporary directory entry */ 949 | if (tmp == NULL) { 950 | tmp = (struct dirent*) malloc(sizeof(struct dirent)); 951 | if (tmp == NULL) { 952 | /* Cannot allocate temporary directory entry */ 953 | result = -1; 954 | break; 955 | } 956 | } 957 | 958 | /* Read directory entry to temporary area */ 959 | if (readdir_r(dir, tmp, &entry) == /*OK*/0) { 960 | 961 | /* Did we get an entry? */ 962 | if (entry != NULL) { 963 | int pass; 964 | 965 | /* Determine whether to include the entry in result */ 966 | if (filter) { 967 | /* Let the filter function decide */ 968 | pass = filter(tmp); 969 | } 970 | else { 971 | /* No filter function, include everything */ 972 | pass = 1; 973 | } 974 | 975 | if (pass) { 976 | /* Store the temporary entry to pointer table */ 977 | files[size++] = tmp; 978 | tmp = NULL; 979 | 980 | /* Keep up with the number of files */ 981 | result++; 982 | } 983 | 984 | } 985 | else { 986 | 987 | /* 988 | * End of directory stream reached => sort entries and 989 | * exit. 990 | */ 991 | qsort(files, size, sizeof(void*), 992 | (int(*) (const void*, const void*)) compare); 993 | break; 994 | 995 | } 996 | 997 | } 998 | else { 999 | /* Error reading directory entry */ 1000 | result = /*Error*/ -1; 1001 | break; 1002 | } 1003 | 1004 | } 1005 | 1006 | } 1007 | else { 1008 | /* Cannot open directory */ 1009 | result = /*Error*/ -1; 1010 | } 1011 | 1012 | /* Release temporary directory entry */ 1013 | free(tmp); 1014 | 1015 | /* Release allocated memory on error */ 1016 | if (result < 0) { 1017 | for (i = 0; i < size; i++) { 1018 | free(files[i]); 1019 | } 1020 | free(files); 1021 | files = NULL; 1022 | } 1023 | 1024 | /* Close directory stream */ 1025 | if (dir) { 1026 | closedir(dir); 1027 | } 1028 | 1029 | /* Pass pointer table to caller */ 1030 | if (namelist) { 1031 | *namelist = files; 1032 | } 1033 | return result; 1034 | } 1035 | 1036 | /* Alphabetical sorting */ 1037 | static int 1038 | alphasort( 1039 | const struct dirent **a, const struct dirent **b) 1040 | { 1041 | return strcoll((*a)->d_name, (*b)->d_name); 1042 | } 1043 | 1044 | /* Sort versions */ 1045 | static int 1046 | versionsort( 1047 | const struct dirent **a, const struct dirent **b) 1048 | { 1049 | /* FIXME: implement strverscmp and use that */ 1050 | return alphasort(a, b); 1051 | } 1052 | 1053 | /* Convert multi-byte string to wide character string */ 1054 | static int 1055 | dirent_mbstowcs_s( 1056 | size_t *pReturnValue, 1057 | wchar_t *wcstr, 1058 | size_t sizeInWords, 1059 | const char *mbstr, 1060 | size_t count) 1061 | { 1062 | int error; 1063 | 1064 | #if defined(_MSC_VER) && _MSC_VER >= 1400 1065 | 1066 | /* Microsoft Visual Studio 2005 or later */ 1067 | error = mbstowcs_s(pReturnValue, wcstr, sizeInWords, mbstr, count); 1068 | 1069 | #else 1070 | 1071 | /* Older Visual Studio or non-Microsoft compiler */ 1072 | size_t n; 1073 | 1074 | /* Convert to wide-character string (or count characters) */ 1075 | n = mbstowcs(wcstr, mbstr, sizeInWords); 1076 | if (!wcstr || n < count) { 1077 | 1078 | /* Zero-terminate output buffer */ 1079 | if (wcstr && sizeInWords) { 1080 | if (n >= sizeInWords) { 1081 | n = sizeInWords - 1; 1082 | } 1083 | wcstr[n] = 0; 1084 | } 1085 | 1086 | /* Length of resulting multi-byte string WITH zero terminator */ 1087 | if (pReturnValue) { 1088 | *pReturnValue = n + 1; 1089 | } 1090 | 1091 | /* Success */ 1092 | error = 0; 1093 | 1094 | } 1095 | else { 1096 | 1097 | /* Could not convert string */ 1098 | error = 1; 1099 | 1100 | } 1101 | 1102 | #endif 1103 | return error; 1104 | } 1105 | 1106 | /* Convert wide-character string to multi-byte string */ 1107 | static int 1108 | dirent_wcstombs_s( 1109 | size_t *pReturnValue, 1110 | char *mbstr, 1111 | size_t sizeInBytes, /* max size of mbstr */ 1112 | const wchar_t *wcstr, 1113 | size_t count) 1114 | { 1115 | int error; 1116 | 1117 | #if defined(_MSC_VER) && _MSC_VER >= 1400 1118 | 1119 | /* Microsoft Visual Studio 2005 or later */ 1120 | error = wcstombs_s(pReturnValue, mbstr, sizeInBytes, wcstr, count); 1121 | 1122 | #else 1123 | 1124 | /* Older Visual Studio or non-Microsoft compiler */ 1125 | size_t n; 1126 | 1127 | /* Convert to multi-byte string (or count the number of bytes needed) */ 1128 | n = wcstombs(mbstr, wcstr, sizeInBytes); 1129 | if (!mbstr || n < count) { 1130 | 1131 | /* Zero-terminate output buffer */ 1132 | if (mbstr && sizeInBytes) { 1133 | if (n >= sizeInBytes) { 1134 | n = sizeInBytes - 1; 1135 | } 1136 | mbstr[n] = '\0'; 1137 | } 1138 | 1139 | /* Length of resulting multi-bytes string WITH zero-terminator */ 1140 | if (pReturnValue) { 1141 | *pReturnValue = n + 1; 1142 | } 1143 | 1144 | /* Success */ 1145 | error = 0; 1146 | 1147 | } 1148 | else { 1149 | 1150 | /* Cannot convert string */ 1151 | error = 1; 1152 | 1153 | } 1154 | 1155 | #endif 1156 | return error; 1157 | } 1158 | 1159 | /* Set errno variable */ 1160 | static void 1161 | dirent_set_errno( 1162 | int error) 1163 | { 1164 | #if defined(_MSC_VER) && _MSC_VER >= 1400 1165 | 1166 | /* Microsoft Visual Studio 2005 and later */ 1167 | _set_errno(error); 1168 | 1169 | #else 1170 | 1171 | /* Non-Microsoft compiler or older Microsoft compiler */ 1172 | errno = error; 1173 | 1174 | #endif 1175 | } 1176 | 1177 | 1178 | #ifdef __cplusplus 1179 | } 1180 | #endif 1181 | #endif /*DIRENT_H*/ 1182 | --------------------------------------------------------------------------------