├── .github └── workflows │ └── go.yml ├── .gitignore ├── LICENSE ├── README.md ├── cmd └── gowasm2cpp │ └── main.go ├── example ├── ebiten │ ├── build.go │ ├── gen.sh │ ├── glfwdriver.cpp │ ├── glfwdriver.h │ ├── main.cpp │ ├── main.go │ └── run.sh ├── goroutine │ ├── main.cpp │ ├── main.go │ └── run.sh └── helloworld │ ├── main.cpp │ ├── main.go │ └── run.sh ├── go.mod ├── go.sum ├── gowasm2cpp ├── bits.go ├── bytes.go ├── game.go ├── generate.go ├── gl.go ├── import.go ├── inst.go ├── js.go ├── mem.go ├── ops.go └── taskqueue.go ├── internal └── stackvar │ ├── stackvars.go │ └── stackvars_test.go └── test └── stdlib ├── main.cpp └── run.sh /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | strategy: 8 | matrix: 9 | go: ['1.16.x', '1.17.x'] 10 | name: Test with Go ${{ matrix.go }} 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v2 15 | 16 | - name: Install dependencies 17 | run: | 18 | sudo apt-get update 19 | sudo apt-get install libgl1-mesa-dev 20 | 21 | - name: Setup Go 22 | uses: actions/setup-go@v2 23 | with: 24 | go-version: ${{ matrix.go }} 25 | stable: false 26 | 27 | - name: Test stdlib 28 | working-directory: test/stdlib 29 | run: | 30 | ./run.sh fmt -test.v 31 | ./run.sh math -test.v 32 | ./run.sh math/big -test.v -test.short # TestLinkerGC doesn't work (#21) 33 | ./run.sh math/bits -test.v 34 | ./run.sh math/cmplx -test.v 35 | ./run.sh math/rand -test.v 36 | ./run.sh runtime -test.v 37 | ./run.sh runtime/debug -test.v 38 | ./run.sh runtime/internal/atomic -test.v 39 | ./run.sh runtime/internal/math -test.v 40 | ./run.sh runtime/internal/sys -test.v 41 | ./run.sh strconv -test.v 42 | ./run.sh strings -test.v 43 | ./run.sh sort -test.v 44 | ./run.sh sync -test.v 45 | ./run.sh sync/atomic -test.v 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.dSYM 3 | *.o 4 | *.wasm 5 | .DS_Store 6 | .cache 7 | .vs 8 | autogen/ 9 | bin/ 10 | obj/ 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go2cpp 2 | 3 | A converter from Go to C++ 4 | 5 | ## Example 6 | 7 | ```sh 8 | cd example/helloworld 9 | ./run.sh 10 | ``` 11 | 12 | ## How does this work? 13 | 14 | This tool analyses a Wasm file compiled from Go files, and generates C++ files based on the Wasm file. 15 | 16 | ## TODO 17 | 18 | * Improving compiling speed by reducing C++ files 19 | * `net/http` 20 | * `os` 21 | -------------------------------------------------------------------------------- /cmd/gowasm2cpp/main.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package main 4 | 5 | import ( 6 | "flag" 7 | "log" 8 | "os" 9 | 10 | "github.com/pkg/profile" 11 | 12 | "github.com/hajimehoshi/go2cpp/gowasm2cpp" 13 | ) 14 | 15 | var ( 16 | flagOut = flag.String("out", ".", "Output directory") 17 | flagInclude = flag.String("include", "", "Include path") 18 | flagWasm = flag.String("wasm", "", "WebAssembly file generated by Go") 19 | flagNamespace = flag.String("namespace", "", "Namespace") 20 | flagProfile = flag.Bool("profile", false, "Take profiles") 21 | ) 22 | 23 | func main() { 24 | flag.Parse() 25 | if *flagProfile { 26 | defer profile.Start().Stop() 27 | } 28 | 29 | if err := os.MkdirAll(*flagOut, 0755); err != nil { 30 | log.Fatal(err) 31 | } 32 | if err := gowasm2cpp.Generate(*flagOut, *flagInclude, *flagWasm, *flagNamespace); err != nil { 33 | log.Fatal(err) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /example/ebiten/build.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // +build ignore 4 | 5 | package main 6 | 7 | import ( 8 | "crypto/sha256" 9 | "encoding/hex" 10 | "fmt" 11 | "os" 12 | "os/exec" 13 | "path/filepath" 14 | "runtime" 15 | "strings" 16 | "sync" 17 | 18 | "golang.org/x/sync/errgroup" 19 | ) 20 | 21 | func main() { 22 | if err := run(); err != nil { 23 | panic(err) 24 | } 25 | } 26 | 27 | func run() error { 28 | out := "ebiten" 29 | _ = os.Remove(out) 30 | 31 | cachedir := ".cache" 32 | if err := os.MkdirAll(cachedir, 0755); err != nil { 33 | return err 34 | } 35 | 36 | srcs, err := filepath.Glob("*.cpp") 37 | if err != nil { 38 | return err 39 | } 40 | autogensrcs, err := filepath.Glob("autogen/*.cpp") 41 | if err != nil { 42 | return err 43 | } 44 | srcs = append(srcs, autogensrcs...) 45 | 46 | var objs []string 47 | var objsm sync.Mutex 48 | var g errgroup.Group 49 | sem := make(chan struct{}, runtime.NumCPU()) 50 | for _, src := range srcs { 51 | src := src 52 | g.Go(func() error { 53 | sem <- struct{}{} 54 | defer func() { 55 | <-sem 56 | }() 57 | 58 | ppcmd := exec.Command("clang++", "-E", "-std=c++14", src) 59 | ppcmd.Stderr = os.Stderr 60 | hash := sha256.New() 61 | ppcmd.Stdout = hash 62 | if err := ppcmd.Run(); err != nil { 63 | return err 64 | } 65 | hashsum := hash.Sum(nil) 66 | obj := filepath.Join(cachedir, hex.EncodeToString(hashsum[:])+".o") 67 | objsm.Lock() 68 | objs = append(objs, obj) 69 | objsm.Unlock() 70 | if _, err := os.Stat(obj); err == nil { 71 | return nil 72 | } else if !os.IsNotExist(err) { 73 | return err 74 | } 75 | 76 | args := []string{"clang++", 77 | "-O3", 78 | "-Wall", 79 | "-std=c++14", 80 | "-pthread", 81 | "-I.", 82 | "-g", 83 | "-c", 84 | "-o", obj, src} 85 | fmt.Println(strings.Join(args, " ")) 86 | buildcmd := exec.Command(args[0], args[1:]...) 87 | buildcmd.Stderr = os.Stderr 88 | if err := buildcmd.Run(); err != nil { 89 | return err 90 | } 91 | return nil 92 | }) 93 | } 94 | 95 | if err := g.Wait(); err != nil { 96 | return err 97 | } 98 | 99 | args := []string{"clang++", 100 | "-Wall", 101 | "-std=c++14", 102 | "-framework", "OpenGL", 103 | "-lglfw", 104 | "-DGL_SILENCE_DEPRECATION", 105 | "-pthread", 106 | "-g", 107 | "-o", out} 108 | for _, obj := range objs { 109 | args = append(args, obj) 110 | } 111 | fmt.Println(strings.Join(args, " ")) 112 | cmd := exec.Command(args[0], args[1:]...) 113 | cmd.Stderr = os.Stderr 114 | if err := cmd.Run(); err != nil { 115 | return err 116 | } 117 | 118 | return nil 119 | } 120 | -------------------------------------------------------------------------------- /example/ebiten/gen.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | env GOOS=js GOARCH=wasm go build -tags example -o ebiten.wasm -trimpath $@ 3 | rm -rf autogen 4 | go run ../../cmd/gowasm2cpp -out autogen -include autogen -wasm ebiten.wasm -namespace go2cpp_autogen 5 | -------------------------------------------------------------------------------- /example/ebiten/glfwdriver.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "glfwdriver.h" 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace { 12 | 13 | constexpr int kWidth = 640; 14 | constexpr int kHeight = 480; 15 | 16 | } // namespace 17 | 18 | bool GLFWDriver::Initialize() { 19 | if (!glfwInit()) { 20 | return false; 21 | } 22 | 23 | glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); 24 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); 25 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); 26 | 27 | // TODO: Close the window at the destructor. 28 | window_ = glfwCreateWindow(kWidth, kHeight, "Ebiten test", nullptr, nullptr); 29 | if (!window_) { 30 | glfwTerminate(); 31 | return false; 32 | } 33 | glfwMakeContextCurrent(window_); 34 | glfwSwapInterval(1); 35 | 36 | int framebuffer_width; 37 | glfwGetFramebufferSize(window_, &framebuffer_width, nullptr); 38 | device_pixel_ratio_ = static_cast(framebuffer_width) / kWidth; 39 | 40 | return true; 41 | } 42 | 43 | bool GLFWDriver::Finalize() { 44 | glfwTerminate(); 45 | return true; 46 | } 47 | 48 | void GLFWDriver::Update(std::function f) { 49 | glfwPollEvents(); 50 | f(); 51 | glfwSwapBuffers(window_); 52 | } 53 | 54 | int GLFWDriver::GetScreenWidth() { return kWidth; } 55 | 56 | int GLFWDriver::GetScreenHeight() { return kHeight; } 57 | 58 | double GLFWDriver::GetDevicePixelRatio() { return device_pixel_ratio_; } 59 | 60 | void *GLFWDriver::GetOpenGLFunction(const char *name) { 61 | return dlsym(RTLD_DEFAULT, name); 62 | } 63 | 64 | std::vector GLFWDriver::GetTouches() { 65 | if (glfwGetMouseButton(window_, GLFW_MOUSE_BUTTON_LEFT) != GLFW_PRESS) { 66 | return {}; 67 | } 68 | 69 | double xpos, ypos; 70 | glfwGetCursorPos(window_, &xpos, &ypos); 71 | go2cpp_autogen::Game::Touch touch; 72 | touch.id = 0; 73 | touch.x = static_cast(xpos); 74 | touch.y = static_cast(ypos); 75 | return {touch}; 76 | } 77 | 78 | std::vector GLFWDriver::GetGamepads() { 79 | std::vector gamepads; 80 | for (int id = GLFW_JOYSTICK_1; id <= GLFW_JOYSTICK_LAST; id++) { 81 | if (!glfwJoystickPresent(id)) { 82 | continue; 83 | } 84 | 85 | go2cpp_autogen::Game::Gamepad gamepad; 86 | gamepad.id = id; 87 | gamepad.standard = false; 88 | 89 | const unsigned char *button_states = 90 | glfwGetJoystickButtons(id, &gamepad.button_count); 91 | constexpr int kButtonMaxCount = 92 | sizeof(gamepad.button_pressed) / sizeof(gamepad.button_pressed[0]); 93 | if (kButtonMaxCount < gamepad.button_count) { 94 | gamepad.button_count = kButtonMaxCount; 95 | } 96 | for (int i = 0; i < gamepad.button_count; i++) { 97 | gamepad.button_pressed[i] = button_states[i] == GLFW_PRESS; 98 | gamepad.button_values[i] = button_states[i] == GLFW_PRESS ? 1.0f : 0.0f; 99 | } 100 | 101 | const float *axis_states = glfwGetJoystickAxes(id, &gamepad.axis_count); 102 | constexpr int kAxisMaxCount = 103 | sizeof(gamepad.axes) / sizeof(gamepad.axes[0]); 104 | if (kAxisMaxCount < gamepad.axis_count) { 105 | gamepad.axis_count = kAxisMaxCount; 106 | } 107 | for (int i = 0; i < gamepad.axis_count; i++) { 108 | gamepad.axes[i] = axis_states[i]; 109 | } 110 | 111 | gamepads.push_back(gamepad); 112 | } 113 | return gamepads; 114 | } 115 | 116 | std::string GLFWDriver::GetLocalStorageItem(const std::string &key) { 117 | return local_storage_[key]; 118 | } 119 | 120 | void GLFWDriver::SetLocalStorageItem(const std::string &key, 121 | const std::string &value) { 122 | local_storage_[key] = value; 123 | } 124 | 125 | void GLFWDriver::OpenAudio(int sample_rate, int channel_num, 126 | int bit_depth_in_bytes) { 127 | sample_rate_ = sample_rate; 128 | channel_num_ = channel_num; 129 | bit_depth_in_bytes_ = bit_depth_in_bytes; 130 | buffer_size_ = sample_rate * channel_num * bit_depth_in_bytes / 2; 131 | } 132 | 133 | void GLFWDriver::CloseAudio() { 134 | } 135 | 136 | std::unique_ptr 137 | GLFWDriver::CreateAudioPlayer(std::function on_written) { 138 | return std::make_unique(sample_rate_, channel_num_, 139 | bit_depth_in_bytes_, buffer_size_, 140 | on_written); 141 | } 142 | 143 | GLFWDriver::AudioPlayer::AudioPlayer(int sample_rate, int channel_num, 144 | int bit_depth_in_bytes, int buffer_size, 145 | std::function on_written) 146 | : sample_rate_{sample_rate}, channel_num_{channel_num}, 147 | bit_depth_in_bytes_{bit_depth_in_bytes}, buffer_size_{buffer_size}, 148 | on_written_{on_written}, thread_{[this] { Loop(); }} {} 149 | 150 | GLFWDriver::AudioPlayer::~AudioPlayer() { 151 | if (thread_.joinable()) { 152 | thread_.join(); 153 | } 154 | } 155 | 156 | void GLFWDriver::AudioPlayer::Close(bool immediately) { 157 | { 158 | std::lock_guard lock{mutex_}; 159 | paused_ = false; 160 | closed_ = true; 161 | } 162 | cond_.notify_all(); 163 | } 164 | 165 | double GLFWDriver::AudioPlayer::GetVolume() { return volume_; } 166 | 167 | void GLFWDriver::AudioPlayer::SetVolume(double volume) { volume_ = volume; } 168 | 169 | void GLFWDriver::AudioPlayer::Pause() { 170 | { 171 | std::lock_guard lock{mutex_}; 172 | if (closed_) { 173 | return; 174 | } 175 | paused_ = true; 176 | } 177 | cond_.notify_all(); 178 | } 179 | 180 | void GLFWDriver::AudioPlayer::Play() { 181 | { 182 | std::lock_guard lock{mutex_}; 183 | if (closed_) { 184 | return; 185 | } 186 | paused_ = false; 187 | } 188 | cond_.notify_all(); 189 | } 190 | 191 | void GLFWDriver::AudioPlayer::Write(const uint8_t *data, int length) { 192 | { 193 | std::unique_lock lock{mutex_}; 194 | cond_.wait(lock, [this] { 195 | return (ready_to_play_ < buffer_size_ || closed_) && !paused_; 196 | }); 197 | if (closed_) { 198 | return; 199 | } 200 | ready_to_play_ += length; 201 | } 202 | cond_.notify_one(); 203 | } 204 | 205 | size_t GLFWDriver::AudioPlayer::GetUnplayedBufferSize() { 206 | std::lock_guard lock{mutex_}; 207 | return ready_to_play_; 208 | } 209 | 210 | void GLFWDriver::AudioPlayer::Loop() { 211 | for (;;) { 212 | { 213 | std::unique_lock lock{mutex_}; 214 | cond_.wait(lock, [this] { 215 | return (ready_to_play_ >= buffer_size_ || closed_) && !paused_; 216 | }); 217 | if (closed_) { 218 | return; 219 | } 220 | ready_to_play_ -= buffer_size_; 221 | on_written_(); 222 | } 223 | cond_.notify_one(); 224 | int bytes_per_sec = sample_rate_ * channel_num_ * bit_depth_in_bytes_; 225 | std::chrono::duration duration(static_cast(buffer_size_) / 226 | static_cast(bytes_per_sec)); 227 | std::this_thread::sleep_for(duration); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /example/ebiten/glfwdriver.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #ifndef GLFWGAME_H 4 | #define GLFWGAME_H 5 | 6 | #include "autogen/game.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | struct GLFWwindow; 17 | 18 | class GLFWDriver : public go2cpp_autogen::Game::Driver { 19 | public: 20 | bool Initialize() override; 21 | bool Finalize() override; 22 | void Update(std::function f) override; 23 | int GetScreenWidth() override; 24 | int GetScreenHeight() override; 25 | double GetDevicePixelRatio() override; 26 | void *GetOpenGLFunction(const char *name) override; 27 | std::vector GetTouches() override; 28 | std::vector GetGamepads() override; 29 | void OpenAudio(int sample_rate, int channel_num, 30 | int bit_depth_in_bytes) override; 31 | void CloseAudio() override; 32 | std::string GetLocalStorageItem(const std::string &key) override; 33 | void SetLocalStorageItem(const std::string &key, 34 | const std::string &value) override; 35 | std::unique_ptr 36 | CreateAudioPlayer(std::function on_written) override; 37 | 38 | private: 39 | class AudioPlayer : public go2cpp_autogen::Game::AudioPlayer { 40 | public: 41 | AudioPlayer(int sample_rate, int channel_num, int bit_depth_in_bytes, 42 | int buffer_size, std::function on_written); 43 | ~AudioPlayer() override; 44 | 45 | void Close(bool immediately) override; 46 | double GetVolume() override; 47 | void SetVolume(double volume) override; 48 | void Pause() override; 49 | void Play() override; 50 | void Write(const uint8_t *data, int length) override; 51 | size_t GetUnplayedBufferSize() override; 52 | 53 | private: 54 | void Loop(); 55 | 56 | const int sample_rate_; 57 | const int channel_num_; 58 | const int bit_depth_in_bytes_; 59 | const int buffer_size_; 60 | std::function on_written_; 61 | double volume_ = 1.0; 62 | int ready_to_play_ = 0; 63 | bool paused_ = false; 64 | bool closed_ = false; 65 | std::mutex mutex_; 66 | std::condition_variable cond_; 67 | std::thread thread_; 68 | }; 69 | 70 | GLFWwindow *window_; 71 | double device_pixel_ratio_; 72 | std::map local_storage_; 73 | int sample_rate_ = 0; 74 | int channel_num_ = 0; 75 | int bit_depth_in_bytes_ = 0; 76 | int buffer_size_ = 0; 77 | }; 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /example/ebiten/main.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "autogen/game.h" 4 | 5 | #include "glfwdriver.h" 6 | 7 | int main() { 8 | go2cpp_autogen::Game game(std::make_unique()); 9 | return game.Run(); 10 | } 11 | -------------------------------------------------------------------------------- /example/ebiten/main.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //go:build tools 4 | // +build tools 5 | 6 | package main 7 | 8 | import ( 9 | _ "github.com/hajimehoshi/go-inovation" 10 | ) 11 | 12 | // This file is for go.mod to keep dependencies. 13 | -------------------------------------------------------------------------------- /example/ebiten/run.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | ./gen.sh github.com/hajimehoshi/go-inovation 3 | go run build.go 4 | ./ebiten 5 | -------------------------------------------------------------------------------- /example/goroutine/main.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "autogen/go.h" 4 | 5 | int main() { 6 | go2cpp_autogen::Go go; 7 | return go.Run(); 8 | } 9 | -------------------------------------------------------------------------------- /example/goroutine/main.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // +build example 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "time" 10 | ) 11 | 12 | func main() { 13 | ch1 := make(chan string) 14 | ch2 := make(chan string) 15 | ch3 := make(chan struct{}) 16 | 17 | go func() { 18 | for { 19 | time.Sleep(100 * time.Millisecond) 20 | ch1 <- "A" 21 | } 22 | }() 23 | go func() { 24 | for { 25 | time.Sleep(200 * time.Millisecond) 26 | ch2 <- "B" 27 | } 28 | }() 29 | go func() { 30 | for { 31 | time.Sleep(3 * time.Second) 32 | close(ch3) 33 | } 34 | }() 35 | 36 | loop: 37 | for { 38 | select { 39 | case str := <-ch1: 40 | fmt.Println(str) 41 | case str := <-ch2: 42 | fmt.Println(str) 43 | case <-ch3: 44 | fmt.Println("Done") 45 | break loop 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /example/goroutine/run.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | env GOOS=js GOARCH=wasm go build -tags example -o goroutine.wasm -trimpath . 3 | rm -rf autogen 4 | go run ../../cmd/gowasm2cpp -out autogen -include autogen -wasm goroutine.wasm -namespace go2cpp_autogen 5 | clang++ -O3 -Wall -std=c++14 -pthread -I. -o goroutine -g *.cpp autogen/*.cpp 6 | ./goroutine 7 | -------------------------------------------------------------------------------- /example/helloworld/main.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #include "autogen/go.h" 4 | 5 | int main() { 6 | go2cpp_autogen::Go go; 7 | return go.Run(); 8 | } 9 | -------------------------------------------------------------------------------- /example/helloworld/main.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // +build example 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | func main() { 12 | fmt.Println("Hello, World!") 13 | } 14 | -------------------------------------------------------------------------------- /example/helloworld/run.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | env GOOS=js GOARCH=wasm go build -tags example -o helloworld.wasm -trimpath . 3 | rm -rf autogen *.o 4 | go run ../../cmd/gowasm2cpp -out autogen -include autogen -wasm helloworld.wasm -namespace go2cpp_autogen 5 | clang++ -O3 -Wall -std=c++14 -pthread -I. -o helloworld -g *.cpp autogen/*.cpp 6 | ./helloworld 7 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/hajimehoshi/go2cpp 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/go-interpreter/wagon v0.6.1-0.20200226200811-4ca95707c808 7 | github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.12 // indirect 8 | github.com/hajimehoshi/go-inovation v0.0.0-20201231064207-9113b5413c76 9 | github.com/pkg/profile v1.4.0 10 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c 11 | ) 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 2 | github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= 3 | github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= 4 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200707082815-5321531c36a2/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 5 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be h1:vEIVIuBApEBQTEJt19GfhoU+zFSV+sNTa9E9FdnRYfk= 6 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 7 | github.com/go-interpreter/wagon v0.6.1-0.20200226200811-4ca95707c808 h1:DK/r87dK66orWskGaYpM/0csKs1uOtwve5tX0z/ZljI= 8 | github.com/go-interpreter/wagon v0.6.1-0.20200226200811-4ca95707c808/go.mod h1:lQUozviuTS6v7A2HXs6L0Wc72Apl9+mKsGLsJOHYiME= 9 | github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= 10 | github.com/hajimehoshi/bitmapfont/v2 v2.1.0/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs= 11 | github.com/hajimehoshi/bitmapfont/v2 v2.1.2/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs= 12 | github.com/hajimehoshi/bitmapfont/v2 v2.1.3 h1:JefUkL0M4nrdVwVq7MMZxSTh6mSxOylm+C4Anoucbb0= 13 | github.com/hajimehoshi/bitmapfont/v2 v2.1.3/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs= 14 | github.com/hajimehoshi/ebiten/v2 v2.0.0-alpha.0.20201010065233-7f55fdf76944/go.mod h1:EvUbs3LB1sybSHLl0cdcCciJ7Uw2KIbtxIO9Y92fsKM= 15 | github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.12 h1:lUfVe3Yqm62bPo4K58alfjTr+yk35+rOGxfxiQzLGys= 16 | github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.12/go.mod h1:B4Cje+Kb1ZjztrKFPaiMWOGXO3Vp8u+zIBdxkZqkyD4= 17 | github.com/hajimehoshi/file2byteslice v0.0.0-20200812174855-0e5e8a80490e/go.mod h1:CqqAHp7Dk/AqQiwuhV1yT2334qbA/tFWQW0MD2dGqUE= 18 | github.com/hajimehoshi/go-inovation v0.0.0-20201231064207-9113b5413c76 h1:FHFhpxbSq5FLgDxeM54S1f2Tp6HWY/FWEiS7Syh7rkM= 19 | github.com/hajimehoshi/go-inovation v0.0.0-20201231064207-9113b5413c76/go.mod h1:RjcTbBraZqituZouaM7K3sxFPyJq+vpgzf3cp1Nv5Vs= 20 | github.com/hajimehoshi/go-mp3 v0.3.1/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM= 21 | github.com/hajimehoshi/go-mp3 v0.3.2/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM= 22 | github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI= 23 | github.com/hajimehoshi/oto v0.6.5 h1:S3xLSfD6J8zhaImumQG7f9E0u12ljoscHqNNmRrQeVE= 24 | github.com/hajimehoshi/oto v0.6.5/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI= 25 | github.com/hajimehoshi/oto/v2 v2.0.0-alpha.2 h1:afKKJ3yH3FARLSwLGukFVMgcA4U9wHBJbkbR2ARjFDA= 26 | github.com/hajimehoshi/oto/v2 v2.0.0-alpha.2/go.mod h1:rUKQmwMkqmRxe+IAof9+tuYA2ofm8cAWXFmSfzDN8vQ= 27 | github.com/jakecoffman/cp v1.0.0/go.mod h1:JjY/Fp6d8E1CHnu74gWNnU0+b9VzEdUVPoJxg2PsTQg= 28 | github.com/jakecoffman/cp v1.1.0/go.mod h1:JjY/Fp6d8E1CHnu74gWNnU0+b9VzEdUVPoJxg2PsTQg= 29 | github.com/jfreymuth/oggvorbis v1.0.1/go.mod h1:NqS+K+UXKje0FUYUPosyQ+XTVvjmVjps1aEZH1sumIk= 30 | github.com/jfreymuth/oggvorbis v1.0.3 h1:MLNGGyhOMiVcvea9Dp5+gbs2SAwqwQbtrWnonYa0M0Y= 31 | github.com/jfreymuth/oggvorbis v1.0.3/go.mod h1:1U4pqWmghcoVsCJJ4fRBKv9peUJMBHixthRlBeD6uII= 32 | github.com/jfreymuth/vorbis v1.0.0/go.mod h1:8zy3lUAm9K/rJJk223RKy6vjCZTWC61NA2QD06bfOE0= 33 | github.com/jfreymuth/vorbis v1.0.2 h1:m1xH6+ZI4thH927pgKD8JOH4eaGRm18rEE9/0WKjvNE= 34 | github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3TWXyuzrqQ= 35 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 36 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 37 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 38 | github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= 39 | github.com/pkg/profile v1.4.0 h1:uCmaf4vVbWAOZz36k1hrQD7ijGRzLwaME8Am/7a4jZI= 40 | github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE= 41 | github.com/twitchyliquid64/golang-asm v0.0.0-20190126203739-365674df15fc h1:RTUQlKzoZZVG3umWNzOYeFecQLIh+dbxXvJp1zPQJTI= 42 | github.com/twitchyliquid64/golang-asm v0.0.0-20190126203739-365674df15fc/go.mod h1:NoCfSFWosfqMqmmD7hApkirIK9ozpHjxRnRxs1l413A= 43 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 44 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 45 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 46 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 47 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 48 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 49 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 50 | golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU= 51 | golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= 52 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 53 | golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 54 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 55 | golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 56 | golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs= 57 | golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= 58 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 59 | golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 60 | golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= 61 | golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 h1:3In5TnfvnuXTF/uflgpYxSCEGP2NdYT37KsPh3VjZYU= 62 | golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554/go.mod h1:jFTmtFYCV0MFtXBU+J5V/+5AUeVS0ON/0WkE/KSrl6E= 63 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 64 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 65 | golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 66 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 67 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 68 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 69 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 70 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 71 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 72 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 73 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 74 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 75 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= 76 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 77 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 78 | golang.org/x/sys v0.0.0-20190306220234-b354f8bf4d9e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 79 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 80 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 81 | golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 82 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 83 | golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 84 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 85 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 86 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 87 | golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55 h1:rw6UNGRMfarCepjI8qOepea/SXwIBVfTKjztZ5gBbq4= 88 | golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 89 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 90 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 91 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 92 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 93 | golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= 94 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 95 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 96 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 97 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 98 | golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 99 | golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= 100 | golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 101 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 102 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 103 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 104 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 105 | -------------------------------------------------------------------------------- /gowasm2cpp/bits.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package gowasm2cpp 4 | 5 | import ( 6 | "os" 7 | "path/filepath" 8 | "text/template" 9 | ) 10 | 11 | func writeBits(dir string, incpath string, namespace string) error { 12 | { 13 | f, err := os.Create(filepath.Join(dir, "bits.h")) 14 | if err != nil { 15 | return err 16 | } 17 | defer f.Close() 18 | 19 | if err := bitsHTmpl.Execute(f, struct { 20 | IncludeGuard string 21 | IncludePath string 22 | Namespace string 23 | }{ 24 | IncludeGuard: includeGuard(namespace) + "_BITS_H", 25 | IncludePath: incpath, 26 | Namespace: namespace, 27 | }); err != nil { 28 | return err 29 | } 30 | } 31 | { 32 | f, err := os.Create(filepath.Join(dir, "bits.cpp")) 33 | if err != nil { 34 | return err 35 | } 36 | defer f.Close() 37 | 38 | if err := bitsCppTmpl.Execute(f, struct { 39 | IncludePath string 40 | Namespace string 41 | }{ 42 | IncludePath: incpath, 43 | Namespace: namespace, 44 | }); err != nil { 45 | return err 46 | } 47 | } 48 | return nil 49 | } 50 | 51 | var bitsHTmpl = template.Must(template.New("bits.h").Parse(`// Code generated by go2cpp. DO NOT EDIT. 52 | 53 | #ifndef {{.IncludeGuard}} 54 | #define {{.IncludeGuard}} 55 | 56 | #include 57 | #include 58 | 59 | namespace {{.Namespace}} { 60 | 61 | class Bits { 62 | public: 63 | static uint32_t RotateLeft(uint32_t x, int32_t k); 64 | static uint64_t RotateLeft(uint64_t x, int32_t k); 65 | }; 66 | 67 | class Math { 68 | public: 69 | static float Round(float x); 70 | static double Round(double x); 71 | }; 72 | 73 | } 74 | 75 | #endif // {{.IncludeGuard}} 76 | `)) 77 | 78 | var bitsCppTmpl = template.Must(template.New("bits.cpp").Parse(`// Code generated by go2cpp. DO NOT EDIT. 79 | 80 | #include "{{.IncludePath}}bits.h" 81 | 82 | namespace {{.Namespace}} { 83 | 84 | // The implementation is copied from the Go standard package math/bits, which is under BSD-style license. 85 | 86 | uint32_t Bits::RotateLeft(uint32_t x, int32_t k) { 87 | constexpr int32_t n = 32; 88 | int32_t s = k & (n - 1); 89 | return x<>(n-s); 90 | } 91 | 92 | uint64_t Bits::RotateLeft(uint64_t x, int32_t k) { 93 | constexpr int32_t n = 64; 94 | int32_t s = k & (n - 1); 95 | return x<>(n-s); 96 | } 97 | 98 | float Math::Round(float x) { 99 | // std::rint is also available, but this requires setting a global state by std::fesetround. 100 | float r = std::round(x); 101 | if (std::abs(x - r) == 0.5f && std::fmod(r, 2.0f) != 0.0f) { 102 | return std::trunc(x); 103 | } 104 | return r; 105 | } 106 | 107 | double Math::Round(double x) { 108 | double r = std::round(x); 109 | if (std::abs(x - r) == 0.5 && std::fmod(r, 2.0) != 0.0) { 110 | return std::trunc(x); 111 | } 112 | return r; 113 | } 114 | 115 | } 116 | `)) 117 | -------------------------------------------------------------------------------- /gowasm2cpp/bytes.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package gowasm2cpp 4 | 5 | import ( 6 | "os" 7 | "path/filepath" 8 | "text/template" 9 | ) 10 | 11 | func writeBytes(dir string, incpath string, namespace string) error { 12 | { 13 | f, err := os.Create(filepath.Join(dir, "bytes.h")) 14 | if err != nil { 15 | return err 16 | } 17 | defer f.Close() 18 | 19 | if err := bytesHTmpl.Execute(f, struct { 20 | IncludeGuard string 21 | IncludePath string 22 | Namespace string 23 | }{ 24 | IncludeGuard: includeGuard(namespace) + "_BYTES_H", 25 | IncludePath: incpath, 26 | Namespace: namespace, 27 | }); err != nil { 28 | return err 29 | } 30 | } 31 | { 32 | f, err := os.Create(filepath.Join(dir, "bytes.cpp")) 33 | if err != nil { 34 | return err 35 | } 36 | defer f.Close() 37 | 38 | if err := bytesCppTmpl.Execute(f, struct { 39 | IncludePath string 40 | Namespace string 41 | }{ 42 | IncludePath: incpath, 43 | Namespace: namespace, 44 | }); err != nil { 45 | return err 46 | } 47 | } 48 | return nil 49 | } 50 | 51 | var bytesHTmpl = template.Must(template.New("bytes.h").Parse(`// Code generated by go2cpp. DO NOT EDIT. 52 | 53 | #ifndef {{.IncludeGuard}} 54 | #define {{.IncludeGuard}} 55 | 56 | #include 57 | #include 58 | 59 | namespace {{.Namespace}} { 60 | 61 | // TODO: Replace this with std::span in the C++20 era. 62 | class BytesSpan { 63 | public: 64 | using size_type = size_t; 65 | using reference = uint8_t&; 66 | using const_reference = const uint8_t&; 67 | using iterator = uint8_t*; 68 | using const_iterator = const uint8_t*; 69 | 70 | BytesSpan(); 71 | BytesSpan(uint8_t* data, size_t size); 72 | BytesSpan(const BytesSpan& span); 73 | BytesSpan& operator=(BytesSpan& span); 74 | 75 | reference operator[](size_type n); 76 | const_reference operator[](size_type n) const; 77 | size_type size() const; 78 | iterator begin(); 79 | const_iterator begin() const; 80 | iterator end(); 81 | const_iterator end() const; 82 | 83 | private: 84 | uint8_t* data_ = nullptr; 85 | size_t size_ = 0; 86 | }; 87 | 88 | } 89 | 90 | #endif // {{.IncludeGuard}} 91 | `)) 92 | 93 | var bytesCppTmpl = template.Must(template.New("bytes.cpp").Parse(`// Code generated by go2cpp. DO NOT EDIT. 94 | 95 | #include "{{.IncludePath}}bytes.h" 96 | 97 | namespace {{.Namespace}} { 98 | 99 | BytesSpan::BytesSpan() = default; 100 | 101 | BytesSpan::BytesSpan(uint8_t* data, size_t size) 102 | : data_(data), 103 | size_(size) { 104 | } 105 | 106 | BytesSpan::BytesSpan(const BytesSpan& span) = default; 107 | 108 | BytesSpan& BytesSpan::operator=(BytesSpan& span) = default; 109 | 110 | BytesSpan::reference BytesSpan::operator[](size_type n) { 111 | return data_[n]; 112 | } 113 | 114 | BytesSpan::const_reference BytesSpan::operator[](size_type n) const { 115 | return data_[n]; 116 | } 117 | 118 | BytesSpan::size_type BytesSpan::size() const { 119 | return size_; 120 | } 121 | 122 | BytesSpan::iterator BytesSpan::begin() { 123 | return data_; 124 | } 125 | 126 | BytesSpan::const_iterator BytesSpan::begin() const { 127 | return data_; 128 | } 129 | 130 | BytesSpan::iterator BytesSpan::end() { 131 | return data_ + size_; 132 | } 133 | 134 | BytesSpan::const_iterator BytesSpan::end() const { 135 | return data_ + size_; 136 | } 137 | 138 | } 139 | `)) 140 | -------------------------------------------------------------------------------- /gowasm2cpp/game.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package gowasm2cpp 4 | 5 | import ( 6 | "os" 7 | "path/filepath" 8 | "text/template" 9 | ) 10 | 11 | func writeGame(dir string, incpath string, namespace string) error { 12 | { 13 | f, err := os.Create(filepath.Join(dir, "game.h")) 14 | if err != nil { 15 | return err 16 | } 17 | defer f.Close() 18 | 19 | if err := gameHTmpl.Execute(f, struct { 20 | IncludeGuard string 21 | IncludePath string 22 | Namespace string 23 | }{ 24 | IncludeGuard: includeGuard(namespace) + "_GAME_H", 25 | IncludePath: incpath, 26 | Namespace: namespace, 27 | }); err != nil { 28 | return err 29 | } 30 | } 31 | { 32 | f, err := os.Create(filepath.Join(dir, "game.cpp")) 33 | if err != nil { 34 | return err 35 | } 36 | defer f.Close() 37 | 38 | if err := gameCppTmpl.Execute(f, struct { 39 | IncludePath string 40 | Namespace string 41 | }{ 42 | IncludePath: incpath, 43 | Namespace: namespace, 44 | }); err != nil { 45 | return err 46 | } 47 | } 48 | return nil 49 | } 50 | 51 | var gameHTmpl = template.Must(template.New("game.h").Parse(`// Code generated by go2cpp. DO NOT EDIT. 52 | 53 | #ifndef {{.IncludeGuard}} 54 | #define {{.IncludeGuard}} 55 | 56 | #include "{{.IncludePath}}go.h" 57 | 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | 64 | namespace {{.Namespace}} { 65 | 66 | class Game { 67 | public: 68 | struct Touch { 69 | int id; 70 | int x; 71 | int y; 72 | }; 73 | 74 | struct Gamepad { 75 | int id; 76 | bool standard; 77 | int button_count; 78 | bool button_pressed[256]; 79 | float button_values[256]; 80 | int axis_count; 81 | float axes[16]; 82 | }; 83 | 84 | class AudioPlayer { 85 | public: 86 | virtual ~AudioPlayer(); 87 | virtual void Close(bool immediately) = 0; 88 | virtual double GetVolume() = 0; 89 | virtual void SetVolume(double volume) = 0; 90 | virtual void Pause() = 0; 91 | virtual void Play() = 0; 92 | virtual void Write(const uint8_t* data, int length) = 0; 93 | virtual size_t GetUnplayedBufferSize() = 0; 94 | }; 95 | 96 | class Driver { 97 | public: 98 | virtual ~Driver(); 99 | virtual void DebugWrite(const std::vector& bytes); 100 | virtual bool Initialize() = 0; 101 | virtual bool Finalize() = 0; 102 | virtual void Update(std::function f) = 0; 103 | virtual int GetScreenWidth() = 0; 104 | virtual int GetScreenHeight() = 0; 105 | virtual double GetDevicePixelRatio() = 0; 106 | virtual void* GetOpenGLFunction(const char* name) = 0; 107 | virtual std::vector GetTouches() = 0; 108 | virtual std::vector GetGamepads() = 0; 109 | virtual std::string GetLocalStorageItem(const std::string& key) = 0; 110 | virtual void SetLocalStorageItem(const std::string& key, const std::string& value) = 0; 111 | virtual std::string GetDefaultLanguage(); 112 | 113 | virtual void OpenAudio(int sample_rate, int channel_num, int bit_depth_in_bytes) = 0; 114 | virtual void CloseAudio() = 0; 115 | virtual std::unique_ptr CreateAudioPlayer(std::function on_written) = 0; 116 | 117 | private: 118 | std::unique_ptr default_debug_writer_; 119 | }; 120 | 121 | class Binding { 122 | public: 123 | virtual ~Binding(); 124 | virtual std::vector Get(const std::string& key) = 0; 125 | virtual void Set(const std::string& key, const uint8_t* data, int length) = 0; 126 | }; 127 | 128 | explicit Game(std::unique_ptr driver); 129 | Game(std::unique_ptr driver, std::unique_ptr binding); 130 | 131 | int Run(); 132 | int Run(int argc, char *argv[]); 133 | int Run(const std::vector& args); 134 | 135 | private: 136 | void Update(Value f); 137 | 138 | std::unique_ptr driver_; 139 | std::vector touches_; 140 | std::vector gamepads_; 141 | std::unique_ptr binding_; 142 | bool is_audio_opened_ = false; 143 | }; 144 | 145 | } 146 | 147 | #endif // {{.IncludeGuard}} 148 | `)) 149 | 150 | var gameCppTmpl = template.Must(template.New("game.cpp").Parse(`// Code generated by go2cpp. DO NOT EDIT. 151 | 152 | #include "{{.IncludePath}}game.h" 153 | 154 | #include "{{.IncludePath}}gl.h" 155 | 156 | #include 157 | #include 158 | 159 | namespace {{.Namespace}} { 160 | 161 | namespace { 162 | 163 | // TODO: This is duplicated with js.go. Unify them? 164 | void Panic(const std::string& msg) { 165 | std::cerr << msg << std::endl; 166 | __builtin_unreachable(); 167 | } 168 | 169 | class DriverDebugWriter : public Writer { 170 | public: 171 | DriverDebugWriter(Game::Driver* driver) 172 | : driver_{driver} { 173 | } 174 | 175 | void Write(const std::vector& bytes) override { 176 | driver_->DebugWrite(bytes); 177 | } 178 | 179 | private: 180 | Game::Driver* driver_; 181 | }; 182 | 183 | class BindingObject : public Object { 184 | public: 185 | explicit BindingObject(Game::Binding* binding) 186 | : binding_{binding} { 187 | } 188 | 189 | Value Get(const std::string& key) override { 190 | auto bytes = binding_->Get(key); 191 | auto u8 = std::make_shared(bytes.size()); 192 | std::memcpy(u8->ToBytes().begin(), &(*bytes.begin()), bytes.size()); 193 | return Value{u8}; 194 | } 195 | 196 | void Set(const std::string& key, Value value) override { 197 | if (value.IsString()) { 198 | auto str = value.ToString(); 199 | binding_->Set(key, reinterpret_cast(&(*str.begin())), str.size()); 200 | return; 201 | } 202 | if (value.IsObject()) { 203 | auto bytes = value.ToBytes(); 204 | binding_->Set(key, &(*bytes.begin()), bytes.size()); 205 | return; 206 | } 207 | Panic("BindingObject::Set: value must be a string or bytes but not: " + value.Inspect()); 208 | } 209 | 210 | std::string ToString() const override { 211 | return "BindingObject"; 212 | } 213 | 214 | private: 215 | Game::Binding* binding_; 216 | }; 217 | 218 | class LocalStorage : public Object { 219 | public: 220 | explicit LocalStorage(Game::Driver* driver) 221 | : driver_{driver} { 222 | } 223 | 224 | Value Get(const std::string& key) override { 225 | if (key == "setItem") { 226 | if (!func_set_item_.IsFunction()) { 227 | func_set_item_ = Value{std::make_shared( 228 | [this](Value self, std::vector args) -> Value { 229 | const std::string& key = args[0].ToString(); 230 | const std::string& value = args[1].ToString(); 231 | driver_->SetLocalStorageItem(key, value); 232 | return Value{}; 233 | })}; 234 | } 235 | return func_set_item_; 236 | } 237 | if (key == "getItem") { 238 | if (!func_get_item_.IsFunction()) { 239 | func_get_item_ = Value{std::make_shared( 240 | [this](Value self, std::vector args) -> Value { 241 | const std::string& key = args[0].ToString(); 242 | const std::string& value = driver_->GetLocalStorageItem(key); 243 | return Value{value}; 244 | })}; 245 | } 246 | return func_get_item_; 247 | } 248 | return Value{}; 249 | } 250 | 251 | std::string ToString() const override { 252 | return "LocalStorage"; 253 | } 254 | 255 | private: 256 | Game::Driver* driver_; 257 | 258 | Value func_set_item_; 259 | Value func_get_item_; 260 | }; 261 | 262 | class Navigator : public Object { 263 | public: 264 | explicit Navigator(Game::Driver* driver) 265 | : driver_{driver} { 266 | } 267 | 268 | Value Get(const std::string& key) override { 269 | if (key == "language") { 270 | return Value{driver_->GetDefaultLanguage()}; 271 | } 272 | if (key == "getGamepads") { 273 | if (!func_get_gamepads_.IsFunction()) { 274 | func_get_gamepads_ = Value{std::make_shared( 275 | [this](Value self, std::vector args) -> Value { 276 | const std::vector& gamepads = driver_->GetGamepads(); 277 | static Value gamepad_values_value{std::vector{}}; 278 | auto& gamepad_values = gamepad_values_value.ToArray(); 279 | if (gamepads.size() != gamepad_values.size()) { 280 | gamepad_values.resize(gamepads.size()); 281 | } 282 | 283 | for (size_t i = 0; i < gamepads.size(); i++) { 284 | if (!gamepad_values[i].IsObject()) { 285 | gamepad_values[i] = Value{std::make_shared(std::map{ 286 | {"index", Value{0.0}}, 287 | {"id", Value{""}}, 288 | {"mapping", Value{""}}, 289 | {"axes", Value{std::vector{}}}, 290 | {"buttons", Value{std::vector{}}}, 291 | })}; 292 | } 293 | 294 | auto& obj = gamepad_values[i].ToObject(); 295 | obj.Set("index", Value{static_cast(gamepads[i].id)}); 296 | obj.Set("id", Value{"go2cpp gamepad " + std::to_string(gamepads[i].id)}); 297 | obj.Set("mapping", Value{gamepads[i].standard ? "standard" : ""}); 298 | 299 | auto& axes = obj.Get("axes").ToArray(); 300 | axes.resize(gamepads[i].axis_count); 301 | for (size_t j = 0; j < axes.size(); j++) { 302 | axes[j] = Value{gamepads[i].axes[j]}; 303 | } 304 | 305 | auto& buttons = obj.Get("buttons").ToArray(); 306 | buttons.resize(gamepads[i].button_count); 307 | for (size_t j = 0; j < buttons.size(); j++) { 308 | if (!buttons[j].IsObject()) { 309 | buttons[j] = Value{std::make_shared(std::map{ 310 | {"pressed", Value{false}}, 311 | {"value", Value{0.0}}, 312 | })}; 313 | } 314 | auto& button_obj = buttons[j].ToObject(); 315 | button_obj.Set("pressed", Value{gamepads[i].button_pressed[j]}); 316 | button_obj.Set("value", Value{gamepads[i].button_values[j]}); 317 | } 318 | } 319 | return gamepad_values_value; 320 | })}; 321 | } 322 | return func_get_gamepads_; 323 | } 324 | return Value{}; 325 | } 326 | 327 | std::string ToString() const override { 328 | return "Navigator"; 329 | } 330 | 331 | private: 332 | Game::Driver* driver_; 333 | 334 | Value func_get_gamepads_; 335 | }; 336 | 337 | class AudioPlayer : public Object { 338 | public: 339 | explicit AudioPlayer(Game::Driver* driver) 340 | : driver_{driver} { 341 | } 342 | 343 | void SetOnWrittenCallback(Value on_written, std::function on_written_callback) { 344 | on_written_ = on_written; 345 | player_ = driver_->CreateAudioPlayer(on_written_callback); 346 | } 347 | 348 | void InvokeOnWrittenCallback() { 349 | if (closed_) { 350 | return; 351 | } 352 | on_written_.ToObject().Invoke({}, {}); 353 | } 354 | 355 | Value Get(const std::string& key) override { 356 | if (key == "volume") { 357 | return Value{player_->GetVolume()}; 358 | } 359 | if (key == "pause") { 360 | if (!func_pause_.IsFunction()) { 361 | func_pause_ = Value{std::make_shared( 362 | [this](Value self, std::vector args) -> Value { 363 | player_->Pause(); 364 | return Value{}; 365 | })}; 366 | } 367 | return func_pause_; 368 | } 369 | if (key == "play") { 370 | if (!func_play_.IsFunction()) { 371 | func_play_ = Value{std::make_shared( 372 | [this](Value self, std::vector args) -> Value { 373 | player_->Play(); 374 | return Value{}; 375 | })}; 376 | } 377 | return func_play_; 378 | } 379 | if (key == "close") { 380 | if (!func_close_.IsFunction()) { 381 | func_close_ = Value{std::make_shared( 382 | [this](Value self, std::vector args) -> Value { 383 | closed_ = true; 384 | bool immediately = args[0].ToBool(); 385 | // Removing a player might cause joining its thread, which can take long. 386 | // Call Close explicitly. 387 | player_->Close(immediately); 388 | return Value{}; 389 | })}; 390 | } 391 | return func_close_; 392 | } 393 | if (key == "write") { 394 | if (!func_write_.IsFunction()) { 395 | func_write_ = Value{std::make_shared( 396 | [this](Value self, std::vector args) -> Value { 397 | BytesSpan buf = args[0].ToBytes(); 398 | int size = static_cast(args[1].ToNumber()); 399 | player_->Write(buf.begin(), size); 400 | return Value{}; 401 | })}; 402 | } 403 | return func_write_; 404 | } 405 | if (key == "unplayedBufferSize") { 406 | return Value{static_cast(player_->GetUnplayedBufferSize())}; 407 | } 408 | return Value{}; 409 | } 410 | 411 | void Set(const std::string& key, Value value) override { 412 | if (key == "volume") { 413 | player_->SetVolume(value.ToNumber()); 414 | return; 415 | } 416 | } 417 | 418 | std::string ToString() const override { 419 | return "AudioPlayer"; 420 | } 421 | 422 | private: 423 | Game::Driver* driver_; 424 | std::unique_ptr player_; 425 | Value buf_; 426 | Value on_written_; 427 | 428 | Value func_pause_; 429 | Value func_play_; 430 | Value func_close_; 431 | Value func_write_; 432 | 433 | bool closed_ = false; 434 | std::mutex mutex_; 435 | }; 436 | 437 | class Audio : public Object { 438 | public: 439 | Audio(Go* go, Game::Driver* driver) 440 | : go_{go}, 441 | driver_{driver} { 442 | } 443 | 444 | Value Get(const std::string& key) override { 445 | if (key == "createPlayer") { 446 | if (!func_create_player_.IsFunction()) { 447 | func_create_player_ = Value{std::make_shared( 448 | [this](Value self, std::vector args) -> Value { 449 | auto p = std::make_shared(driver_); 450 | // Capture the shared pointer and use it in the lambda. 451 | // The AudioPlayer must exist when invoking. 452 | p->SetOnWrittenCallback(args[0], [this, p]() { 453 | // This callback can be invoked from a different thread. Use EnqueueTask here. 454 | go_->EnqueueTask([p]() { 455 | p->InvokeOnWrittenCallback(); 456 | }); 457 | }); 458 | return Value{p}; 459 | })}; 460 | } 461 | return func_create_player_; 462 | } 463 | return Value{}; 464 | } 465 | 466 | std::string ToString() const override { 467 | return "Audio"; 468 | } 469 | 470 | private: 471 | Go* go_; 472 | Game::Driver* driver_; 473 | 474 | Value func_create_player_; 475 | }; 476 | 477 | } // namespace 478 | 479 | Game::AudioPlayer::~AudioPlayer() = default; 480 | 481 | Game::Driver::~Driver() = default; 482 | 483 | void Game::Driver::DebugWrite(const std::vector& bytes) { 484 | if (!default_debug_writer_) { 485 | default_debug_writer_ = std::make_unique(std::cerr); 486 | } 487 | default_debug_writer_->Write(bytes); 488 | } 489 | 490 | std::string Game::Driver::GetDefaultLanguage() { 491 | return "en"; 492 | } 493 | 494 | Game::Game(std::unique_ptr driver) 495 | : Game(std::move(driver), nullptr) { 496 | } 497 | 498 | Game::Game(std::unique_ptr driver, std::unique_ptr binding) 499 | : driver_{std::move(driver)}, 500 | binding_{std::move(binding)} { 501 | } 502 | 503 | int Game::Run() { 504 | return Run({}); 505 | } 506 | 507 | int Game::Run(int argc, char *argv[]) { 508 | std::vector args(argv, argv + argc); 509 | return Run(args); 510 | } 511 | 512 | int Game::Run(const std::vector& args) { 513 | if (!driver_->Initialize()) { 514 | return EXIT_FAILURE; 515 | } 516 | 517 | auto& global = Value::Global().ToObject(); 518 | global.Set("localStorage", Value{std::make_shared(driver_.get())}); 519 | global.Set("navigator", Value{std::make_shared(driver_.get())}); 520 | 521 | auto go2cpp = std::make_shared(); 522 | global.Set("go2cpp", Value{go2cpp}); 523 | 524 | auto gl = std::make_shared([this](const char* name) -> void* { 525 | return driver_->GetOpenGLFunction(name); 526 | }); 527 | go2cpp->Set("gl", Value{gl}); 528 | 529 | go2cpp->Set("screenWidth", 530 | Value{static_cast(driver_->GetScreenWidth())}); 531 | go2cpp->Set("screenHeight", 532 | Value{static_cast(driver_->GetScreenHeight())}); 533 | go2cpp->Set("devicePixelRatio", Value{driver_->GetDevicePixelRatio()}); 534 | 535 | go2cpp->Set("touchCount", Value{0.0}); 536 | go2cpp->Set("getTouchId", Value{std::make_shared( 537 | [this](Value self, std::vector args) -> Value { 538 | int idx = static_cast(args[0].ToNumber()); 539 | return Value{static_cast(touches_[idx].id)}; 540 | })}); 541 | go2cpp->Set("getTouchX", Value{std::make_shared( 542 | [this](Value self, std::vector args) -> Value { 543 | int idx = static_cast(args[0].ToNumber()); 544 | return Value{static_cast(touches_[idx].x)}; 545 | })}); 546 | go2cpp->Set("getTouchY", Value{std::make_shared( 547 | [this](Value self, std::vector args) -> Value { 548 | int idx = static_cast(args[0].ToNumber()); 549 | return Value{static_cast(touches_[idx].y)}; 550 | })}); 551 | 552 | Go go{std::make_unique(driver_.get())}; 553 | 554 | go2cpp->Set("createAudio", Value{std::make_shared( 555 | [this, &go](Value self, std::vector args) -> Value { 556 | int sample_rate = static_cast(args[0].ToNumber()); 557 | int channel_num = static_cast(args[1].ToNumber()); 558 | int bit_depth_in_bytes = static_cast(args[2].ToNumber()); 559 | 560 | driver_->OpenAudio(sample_rate, channel_num, bit_depth_in_bytes); 561 | is_audio_opened_ = true; 562 | return Value{std::make_shared